summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderInline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderInline.cpp')
-rw-r--r--Source/WebCore/rendering/RenderInline.cpp1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderInline.cpp b/Source/WebCore/rendering/RenderInline.cpp
new file mode 100644
index 000000000..4b8556bc7
--- /dev/null
+++ b/Source/WebCore/rendering/RenderInline.cpp
@@ -0,0 +1,1565 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * 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 "Chrome.h"
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "HitTestResult.h"
+#include "InlineTextBox.h"
+#include "Page.h"
+#include "RenderArena.h"
+#include "RenderBlock.h"
+#include "RenderLayer.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "TransformState.h"
+#include "VisiblePosition.h"
+
+#if ENABLE(DASHBOARD_SUPPORT)
+#include "Frame.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+RenderInline::RenderInline(Node* node)
+ : RenderBoxModelObject(node)
+ , m_lineHeight(-1)
+ , m_alwaysCreateLineBoxes(false)
+{
+ setChildrenInline(true);
+}
+
+void RenderInline::willBeDestroyed()
+{
+#if !ASSERT_DISABLED
+ // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
+ if (parent() && style()->visibility() == VISIBLE && hasOutline()) {
+ bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
+ if (containingBlockPaintsContinuationOutline) {
+ if (RenderBlock* cb = containingBlock()) {
+ if (RenderBlock* cbCb = cb->containingBlock())
+ ASSERT(!cbCb->paintsContinuationOutline(this));
+ }
+ }
+ }
+#endif
+
+ // 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.
+ RenderBoxModelObject* continuation = this->continuation();
+ if (continuation) {
+ continuation->destroy();
+ setContinuation(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 FrameSelection 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 (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
+ box->remove();
+ }
+ } else if (parent())
+ parent()->dirtyLinesFromChangedChild(this);
+ }
+
+ m_lineBoxes.deleteLineBoxes(renderArena());
+
+ RenderBoxModelObject::willBeDestroyed();
+}
+
+RenderInline* RenderInline::inlineElementContinuation() const
+{
+ RenderBoxModelObject* continuation = this->continuation();
+ if (!continuation || continuation->isInline())
+ return toRenderInline(continuation);
+ return toRenderBlock(continuation)->inlineElementContinuation();
+}
+
+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 = inlineElementContinuation(); currCont; currCont = currCont->inlineElementContinuation()) {
+ RenderBoxModelObject* nextCont = currCont->continuation();
+ currCont->setContinuation(0);
+ currCont->setStyle(style());
+ currCont->setContinuation(nextCont);
+ }
+
+ m_lineHeight = -1;
+
+ if (!m_alwaysCreateLineBoxes) {
+ bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasBoxDecorations() || style()->hasPadding() || style()->hasMargin() || style()->hasOutline();
+ if (oldStyle && alwaysCreateLineBoxes) {
+ dirtyLineBoxes(false);
+ setNeedsLayout(true);
+ }
+ m_alwaysCreateLineBoxes = alwaysCreateLineBoxes;
+ }
+
+ // Update pseudos for :before and :after now.
+ if (!isAnonymous() && document()->usesBeforeAfterRules()) {
+ children()->updateBeforeAfterContent(this, BEFORE);
+ children()->updateBeforeAfterContent(this, AFTER);
+ }
+}
+
+void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
+{
+ // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
+ // background color will only cause a layout on the first rollover.
+ if (m_alwaysCreateLineBoxes)
+ return;
+
+ RenderStyle* parentStyle = parent()->style();
+ RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
+ bool checkFonts = document()->inNoQuirksMode();
+ bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
+ || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
+ || style()->verticalAlign() != BASELINE
+ || style()->textEmphasisMark() != TextEmphasisMarkNone
+ || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
+ || parentStyle->lineHeight() != style()->lineHeight()));
+
+ if (!alwaysCreateLineBoxes && checkFonts && document()->usesFirstLineRules()) {
+ // Have to check the first line style as well.
+ parentStyle = parent()->style(true);
+ RenderStyle* childStyle = style(true);
+ alwaysCreateLineBoxes = !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics())
+ || childStyle->verticalAlign() != BASELINE
+ || parentStyle->lineHeight() != childStyle->lineHeight();
+ }
+
+ if (alwaysCreateLineBoxes) {
+ if (!fullLayout)
+ dirtyLineBoxes(false);
+ m_alwaysCreateLineBoxes = 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)->inlineElementContinuation();
+}
+
+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::createAnonymousStyle(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->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.
+ RenderBoxModelObject* curr = toRenderBoxModelObject(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(inlineCurr, 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 = toRenderBoxModelObject(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 = toRenderBoxModelObject(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, const LayoutPoint& paintOffset)
+{
+ m_lineBoxes.paint(this, paintInfo, paintOffset);
+}
+
+void RenderInline::absoluteRects(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset) const
+{
+ if (!alwaysCreateLineBoxes())
+ culledInlineAbsoluteRects(this, rects, toLayoutSize(accumulatedOffset));
+ else if (InlineFlowBox* curr = firstLineBox()) {
+ for (; curr; curr = curr->nextLineBox())
+ rects.append(enclosingLayoutRect(FloatRect(accumulatedOffset + curr->topLeft(), curr->size())));
+ } else
+ rects.append(LayoutRect(accumulatedOffset, LayoutSize()));
+
+ if (continuation()) {
+ if (continuation()->isBox()) {
+ RenderBox* box = toRenderBox(continuation());
+ continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->size()));
+ } else
+ continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
+ }
+}
+
+void RenderInline::culledInlineAbsoluteRects(const RenderInline* container, Vector<LayoutRect>& rects, const LayoutSize& offset) const
+{
+ if (!culledInlineFirstLineBox()) {
+ rects.append(IntRect(offset.width(), offset.height(), 0, 0));
+ return;
+ }
+
+ bool isHorizontal = style()->isHorizontalWritingMode();
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
+ // direction (aligned to the root box's baseline).
+ if (curr->isBox()) {
+ RenderBox* currBox = toRenderBox(curr);
+ if (currBox->inlineBoxWrapper()) {
+ RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(offset.width() + currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), offset.height() + logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight);
+ else
+ result = FloatRect(offset.width() + logicalTop, offset.height() + currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom());
+ rects.append(enclosingIntRect(result));
+ }
+ } else if (curr->isRenderInline()) {
+ // If the child doesn't need line boxes either, then we can recur.
+ RenderInline* currInline = toRenderInline(curr);
+ if (!currInline->alwaysCreateLineBoxes())
+ currInline->culledInlineAbsoluteRects(container, rects, offset);
+ else {
+ for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
+ RootInlineBox* rootBox = childLine->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(offset.width() + childLine->x() - childLine->marginLogicalLeft(),
+ offset.height() + logicalTop,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
+ logicalHeight);
+ else
+ result = FloatRect(offset.width() + logicalTop,
+ offset.height() + childLine->y() - childLine->marginLogicalLeft(),
+ logicalHeight,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight());
+ rects.append(enclosingIntRect(result));
+ }
+ }
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
+ RootInlineBox* rootBox = childText->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(offset.width() + childText->x(), offset.height() + logicalTop, childText->logicalWidth(), logicalHeight);
+ else
+ result = FloatRect(offset.width() + logicalTop, offset.height() + childText->y(), logicalHeight, childText->logicalWidth());
+ rects.append(enclosingIntRect(result));
+ }
+ }
+ }
+}
+
+void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
+{
+ if (!alwaysCreateLineBoxes())
+ culledInlineAbsoluteQuads(this, quads);
+ else if (InlineFlowBox* curr = firstLineBox()) {
+ for (; curr; curr = curr->nextLineBox()) {
+ FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height());
+ quads.append(localToAbsoluteQuad(localRect, false, wasFixed));
+ }
+ } else
+ quads.append(localToAbsoluteQuad(FloatRect(), false, wasFixed));
+
+ if (continuation())
+ continuation()->absoluteQuads(quads, wasFixed);
+}
+
+void RenderInline::culledInlineAbsoluteQuads(const RenderInline* container, Vector<FloatQuad>& quads) const
+{
+ if (!culledInlineFirstLineBox()) {
+ quads.append(localToAbsoluteQuad(FloatRect()));
+ return;
+ }
+
+ bool isHorizontal = style()->isHorizontalWritingMode();
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
+ // direction (aligned to the root box's baseline).
+ if (curr->isBox()) {
+ RenderBox* currBox = toRenderBox(curr);
+ if (currBox->inlineBoxWrapper()) {
+ RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight);
+ else
+ result = FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom());
+ quads.append(localToAbsoluteQuad(result));
+ }
+ } else if (curr->isRenderInline()) {
+ // If the child doesn't need line boxes either, then we can recur.
+ RenderInline* currInline = toRenderInline(curr);
+ if (!currInline->alwaysCreateLineBoxes())
+ currInline->culledInlineAbsoluteQuads(container, quads);
+ else {
+ for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
+ RootInlineBox* rootBox = childLine->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(childLine->x() - childLine->marginLogicalLeft(),
+ logicalTop,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
+ logicalHeight);
+ else
+ result = FloatRect(logicalTop,
+ childLine->y() - childLine->marginLogicalLeft(),
+ logicalHeight,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight());
+ quads.append(localToAbsoluteQuad(result));
+ }
+ }
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
+ RootInlineBox* rootBox = childText->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ FloatRect result;
+ if (isHorizontal)
+ result = FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight);
+ else
+ result = FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth());
+ quads.append(localToAbsoluteQuad(result));
+ }
+ }
+ }
+}
+
+LayoutUnit RenderInline::offsetLeft() const
+{
+ LayoutUnit x = RenderBoxModelObject::offsetLeft();
+ if (InlineBox* firstBox = firstLineBoxIncludingCulling())
+ x += firstBox->x();
+ return x;
+}
+
+LayoutUnit RenderInline::offsetTop() const
+{
+ LayoutUnit y = RenderBoxModelObject::offsetTop();
+ if (InlineBox* firstBox = firstLineBoxIncludingCulling())
+ y += firstBox->y();
+ return y;
+}
+
+static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
+{
+ if (margin.isAuto())
+ return 0;
+ if (margin.isFixed())
+ return margin.value();
+ if (margin.isPercent())
+ return margin.calcMinValue(max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
+ return 0;
+}
+
+LayoutUnit RenderInline::marginLeft() const
+{
+ return computeMargin(this, style()->marginLeft());
+}
+
+LayoutUnit RenderInline::marginRight() const
+{
+ return computeMargin(this, style()->marginRight());
+}
+
+LayoutUnit RenderInline::marginTop() const
+{
+ return computeMargin(this, style()->marginTop());
+}
+
+LayoutUnit RenderInline::marginBottom() const
+{
+ return computeMargin(this, style()->marginBottom());
+}
+
+LayoutUnit RenderInline::marginStart() const
+{
+ return computeMargin(this, style()->marginStart());
+}
+
+LayoutUnit RenderInline::marginEnd() const
+{
+ return computeMargin(this, style()->marginEnd());
+}
+
+LayoutUnit RenderInline::marginBefore() const
+{
+ return computeMargin(this, style()->marginBefore());
+}
+
+LayoutUnit RenderInline::marginAfter() const
+{
+ return computeMargin(this, style()->marginAfter());
+}
+
+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,
+ const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
+{
+ return m_lineBoxes.hitTest(this, request, result, pointInContainer, accumulatedOffset, hitTestAction);
+}
+
+VisiblePosition RenderInline::positionForPoint(const LayoutPoint& 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.
+ LayoutPoint parentBlockPoint = cb->location() + point;
+ RenderBoxModelObject* c = continuation();
+ while (c) {
+ RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
+ if (c->isInline() || c->firstChild())
+ return c->positionForPoint(parentBlockPoint - contBlock->locationOffset());
+ c = toRenderBlock(c)->inlineElementContinuation();
+ }
+
+ return RenderBoxModelObject::positionForPoint(point);
+}
+
+LayoutRect RenderInline::linesBoundingBox() const
+{
+ if (!alwaysCreateLineBoxes()) {
+ ASSERT(!firstLineBox());
+ return enclosingLayoutRect(culledInlineBoundingBox(this));
+ }
+
+ LayoutRect 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.
+ float logicalLeftSide = 0;
+ float logicalRightSide = 0;
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
+ logicalLeftSide = curr->logicalLeft();
+ if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
+ logicalRightSide = curr->logicalRight();
+ }
+
+ bool isHorizontal = style()->isHorizontalWritingMode();
+
+ float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
+ float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
+ float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
+ float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
+ result = enclosingLayoutRect(FloatRect(x, y, width, height));
+ }
+
+ return result;
+}
+
+FloatRect RenderInline::culledInlineBoundingBox(const RenderInline* container) const
+{
+ FloatRect result;
+ bool isHorizontal = style()->isHorizontalWritingMode();
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
+ // direction (aligned to the root box's baseline).
+ if (curr->isBox()) {
+ RenderBox* currBox = toRenderBox(curr);
+ if (currBox->inlineBoxWrapper()) {
+ RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ if (isHorizontal)
+ result.uniteIfNonZero(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight));
+ else
+ result.uniteIfNonZero(FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom()));
+ }
+ } else if (curr->isRenderInline()) {
+ // If the child doesn't need line boxes either, then we can recur.
+ RenderInline* currInline = toRenderInline(curr);
+ if (!currInline->alwaysCreateLineBoxes())
+ result.uniteIfNonZero(currInline->culledInlineBoundingBox(container));
+ else {
+ for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
+ RootInlineBox* rootBox = childLine->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ if (isHorizontal)
+ result.uniteIfNonZero(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
+ logicalTop,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
+ logicalHeight));
+ else
+ result.uniteIfNonZero(FloatRect(logicalTop,
+ childLine->y() - childLine->marginLogicalLeft(),
+ logicalHeight,
+ childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
+
+ }
+ }
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
+ RootInlineBox* rootBox = childText->root();
+ int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
+ int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
+ if (isHorizontal)
+ result.uniteIfNonZero(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
+ else
+ result.uniteIfNonZero(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
+ }
+ }
+ }
+ return enclosingLayoutRect(result);
+}
+
+InlineBox* RenderInline::culledInlineFirstLineBox() const
+{
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
+ // direction (aligned to the root box's baseline).
+ if (curr->isBox())
+ return toRenderBox(curr)->inlineBoxWrapper();
+ if (curr->isRenderInline()) {
+ RenderInline* currInline = toRenderInline(curr);
+ InlineBox* result = currInline->firstLineBoxIncludingCulling();
+ if (result)
+ return result;
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ if (currText->firstTextBox())
+ return currText->firstTextBox();
+ }
+ }
+ return 0;
+}
+
+InlineBox* RenderInline::culledInlineLastLineBox() const
+{
+ for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
+ // direction (aligned to the root box's baseline).
+ if (curr->isBox())
+ return toRenderBox(curr)->inlineBoxWrapper();
+ if (curr->isRenderInline()) {
+ RenderInline* currInline = toRenderInline(curr);
+ InlineBox* result = currInline->lastLineBoxIncludingCulling();
+ if (result)
+ return result;
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ if (currText->lastTextBox())
+ return currText->lastTextBox();
+ }
+ }
+ return 0;
+}
+
+LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
+{
+ LayoutRect result(culledInlineBoundingBox(this));
+ bool isHorizontal = style()->isHorizontalWritingMode();
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ // For overflow we just have to propagate by hand and recompute it all.
+ if (curr->isBox()) {
+ RenderBox* currBox = toRenderBox(curr);
+ if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
+ LayoutRect logicalRect = currBox->logicalVisualOverflowRectForPropagation(style());
+ if (isHorizontal) {
+ logicalRect.moveBy(currBox->location());
+ result.uniteIfNonZero(logicalRect);
+ } else {
+ logicalRect.moveBy(currBox->location());
+ result.uniteIfNonZero(logicalRect.transposedRect());
+ }
+ }
+ } else if (curr->isRenderInline()) {
+ // If the child doesn't need line boxes either, then we can recur.
+ RenderInline* currInline = toRenderInline(curr);
+ if (!currInline->alwaysCreateLineBoxes())
+ result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
+ else if (!currInline->hasSelfPaintingLayer())
+ result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
+ } else if (curr->isText()) {
+ // FIXME; Overflow from text boxes is lost. We will need to cache this information in
+ // InlineTextBoxes.
+ RenderText* currText = toRenderText(curr);
+ result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
+ }
+ }
+ return result;
+}
+
+LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
+{
+ if (!alwaysCreateLineBoxes())
+ return culledInlineVisualOverflowBoundingBox();
+
+ if (!firstLineBox() || !lastLineBox())
+ return LayoutRect();
+
+ // Return the width of the minimal left side and the maximal right side.
+ LayoutUnit logicalLeftSide = numeric_limits<LayoutUnit>::max();
+ LayoutUnit logicalRightSide = numeric_limits<LayoutUnit>::min();
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
+ logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
+ }
+
+ RootInlineBox* firstRootBox = firstLineBox()->root();
+ RootInlineBox* lastRootBox = lastLineBox()->root();
+
+ LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox->lineTop());
+ LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
+ LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox->lineBottom()) - logicalTop;
+
+ LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
+ if (!style()->isHorizontalWritingMode())
+ rect = rect.transposedRect();
+ return rect;
+}
+
+LayoutRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) const
+{
+ // Only run-ins are allowed in here during layout.
+ ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
+
+ if (!firstLineBoxIncludingCulling() && !continuation())
+ return LayoutRect();
+
+ // Find our leftmost position.
+ LayoutRect boundingBox(linesVisualOverflowBoundingBox());
+ LayoutUnit left = boundingBox.x();
+ LayoutUnit top = boundingBox.y();
+
+ // Now invalidate a rectangle.
+ LayoutUnit 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 (const RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
+ inlineFlow = inlineFlow->parent()) {
+ if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
+ toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top);
+ }
+
+ LayoutRect 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.
+ LayoutRect repaintRect(r);
+ repaintRect.move(-cb->layer()->scrolledContentOffset()); // For overflow:auto/scroll/hidden.
+
+ LayoutRect boxRect(LayoutPoint(), cb->layer()->size());
+ 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()) {
+ LayoutRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow);
+ r.unite(childRect);
+ }
+ }
+
+ if (continuation() && !continuation()->isInline()) {
+ LayoutRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow);
+ r.unite(contRect);
+ }
+ }
+
+ return r;
+}
+
+LayoutRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, LayoutUnit outlineWidth) const
+{
+ LayoutRect 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, LayoutRect& rect, bool fixed) const
+{
+ 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_paintOffset);
+ if (layoutState->m_clipped)
+ rect.intersect(layoutState->m_clipRect);
+ return;
+ }
+ }
+
+ if (repaintContainer == this)
+ return;
+
+ bool containerSkipped;
+ RenderObject* o = container(repaintContainer, &containerSkipped);
+ if (!o)
+ return;
+
+ LayoutPoint topLeft = rect.location();
+
+ if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
+ RenderBlock* cb = toRenderBlock(o);
+ if (cb->hasColumns()) {
+ LayoutRect repaintRect(topLeft, rect.size());
+ cb->adjustRectForColumns(repaintRect);
+ topLeft = repaintRect.location();
+ rect = repaintRect;
+ }
+ }
+
+#if ENABLE(CSS_FILTERS)
+ if (style()->hasFilterOutsets()) {
+ LayoutUnit topOutset;
+ LayoutUnit rightOutset;
+ LayoutUnit bottomOutset;
+ LayoutUnit leftOutset;
+ style()->filter().getOutsets(topOutset, rightOutset, bottomOutset, leftOutset);
+ rect.move(-leftOutset, -topOutset);
+ rect.expand(leftOutset + rightOutset, topOutset + bottomOutset);
+ }
+#endif
+
+ 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.
+
+ LayoutRect repaintRect(topLeft, rect.size());
+ LayoutRect boxRect(LayoutPoint(), containerBox->layer()->size());
+ rect = intersection(repaintRect, boxRect);
+ if (rect.isEmpty())
+ return;
+ } else
+ rect.setLocation(topLeft);
+
+ if (containerSkipped) {
+ // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
+ LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
+ rect.move(-containerOffset);
+ return;
+ }
+
+ o->computeRectForRepaint(repaintContainer, rect, fixed);
+}
+
+LayoutSize RenderInline::offsetFromContainer(RenderObject* container, const LayoutPoint& point) const
+{
+ ASSERT(container == this->container());
+
+ LayoutSize offset;
+ if (isRelPositioned())
+ offset += relativePositionOffset();
+
+ container->adjustForColumns(offset, point);
+
+ if (container->hasOverflowClip())
+ offset -= toRenderBox(container)->layer()->scrolledContentOffset();
+
+ return offset;
+}
+
+void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState, bool* wasFixed) const
+{
+ if (repaintContainer == this)
+ return;
+
+ if (RenderView *v = view()) {
+ if (v->layoutStateEnabled() && !repaintContainer) {
+ LayoutState* layoutState = v->layoutState();
+ LayoutSize offset = layoutState->m_paintOffset;
+ if (style()->position() == RelativePosition && layer())
+ offset += layer()->relativePositionOffset();
+ transformState.move(offset);
+ return;
+ }
+ }
+
+ bool containerSkipped;
+ RenderObject* o = container(repaintContainer, &containerSkipped);
+ if (!o)
+ return;
+
+ IntPoint centerPoint = roundedIntPoint(transformState.mappedPoint());
+ if (o->isBox() && o->style()->isFlippedBlocksWritingMode())
+ transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedIntPoint(transformState.mappedPoint())) - centerPoint);
+
+ LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
+
+ bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
+ if (useTransforms && shouldUseTransformFromContainer(o)) {
+ TransformationMatrix t;
+ getTransformFromContainer(o, containerOffset, t);
+ transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
+ } else
+ transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
+
+ if (containerSkipped) {
+ // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
+ // to just subtract the delta between the repaintContainer and o.
+ LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
+ transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
+ return;
+ }
+
+ o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState, wasFixed);
+}
+
+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 LayoutPoint& point)
+{
+ if (result.innerNode())
+ return;
+
+ Node* n = node();
+ LayoutPoint localPoint(point);
+ if (n) {
+ if (isInlineElementContinuation()) {
+ // 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());
+ return;
+ }
+
+ if (!alwaysCreateLineBoxes()) {
+ // We have to grovel into our children in order to dirty the appropriate lines.
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+ if (curr->isBox() && !curr->needsLayout()) {
+ RenderBox* currBox = toRenderBox(curr);
+ if (currBox->inlineBoxWrapper())
+ currBox->inlineBoxWrapper()->root()->markDirty();
+ } else if (!curr->selfNeedsLayout()) {
+ if (curr->isRenderInline()) {
+ RenderInline* currInline = toRenderInline(curr);
+ for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
+ childLine->root()->markDirty();
+ } else if (curr->isText()) {
+ RenderText* currText = toRenderText(curr);
+ for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
+ childText->root()->markDirty();
+ }
+ }
+ }
+ } else
+ m_lineBoxes.dirtyLineBoxes();
+}
+
+InlineFlowBox* RenderInline::createInlineFlowBox()
+{
+ return new (renderArena()) InlineFlowBox(this);
+}
+
+InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
+{
+ setAlwaysCreateLineBoxes();
+ InlineFlowBox* flowBox = createInlineFlowBox();
+ m_lineBoxes.appendLineBox(flowBox);
+ return flowBox;
+}
+
+LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) 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;
+}
+
+LayoutUnit RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
+{
+ const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
+ return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
+}
+
+LayoutSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const
+{
+ // FIXME: This function isn't right with mixed writing modes.
+
+ ASSERT(isRelPositioned());
+ if (!isRelPositioned())
+ return LayoutSize();
+
+ // 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.
+
+ LayoutSize logicalOffset;
+ LayoutUnit inlinePosition;
+ LayoutUnit blockPosition;
+ if (firstLineBox()) {
+ inlinePosition = roundedLayoutUnit(firstLineBox()->logicalLeft());
+ blockPosition = firstLineBox()->logicalTop();
+ } else {
+ inlinePosition = layer()->staticInlinePosition();
+ blockPosition = layer()->staticBlockPosition();
+ }
+
+ if (!child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
+ logicalOffset.setWidth(inlinePosition);
+
+ // 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.
+ logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
+
+ if (!child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
+ logicalOffset.setHeight(blockPosition);
+
+ return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
+}
+
+void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
+{
+ if (!parent())
+ return;
+
+ // FIXME: We can do better.
+ repaint();
+}
+
+void RenderInline::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset)
+{
+ if (!alwaysCreateLineBoxes())
+ culledInlineAbsoluteRects(this, rects, toLayoutSize(additionalOffset));
+ else {
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
+ rects.append(enclosingLayoutRect(FloatRect(additionalOffset.x() + curr->x(), additionalOffset.y() + curr->y(), curr->width(), curr->height())));
+ }
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (!curr->isText() && !curr->isListMarker()) {
+ FloatPoint pos(additionalOffset);
+ // 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(rects, flooredIntPoint(pos));
+ }
+ }
+
+ if (continuation()) {
+ if (continuation()->isInline())
+ continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + continuation()->containingBlock()->location() - containingBlock()->location()));
+ else
+ continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + toRenderBox(continuation())->location() - containingBlock()->location()));
+ }
+}
+
+void RenderInline::paintOutline(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset)
+{
+ if (!hasOutline())
+ return;
+
+ RenderStyle* styleToUse = style();
+ if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
+ if (!theme()->supportsFocusRing(styleToUse)) {
+ // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
+ paintFocusRing(graphicsContext, paintOffset, styleToUse);
+ }
+ }
+
+ if (graphicsContext->paintingDisabled())
+ return;
+
+ if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE)
+ return;
+
+ Vector<LayoutRect> rects;
+
+ rects.append(LayoutRect());
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ RootInlineBox* root = curr->root();
+ LayoutUnit top = max<LayoutUnit>(root->lineTop(), curr->logicalTop());
+ LayoutUnit bottom = min<LayoutUnit>(root->lineBottom(), curr->logicalBottom());
+ rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
+ }
+ rects.append(LayoutRect());
+
+ Color outlineColor = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
+ bool useTransparencyLayer = outlineColor.hasAlpha();
+ if (useTransparencyLayer) {
+ graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
+ outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
+ }
+
+ for (unsigned i = 1; i < rects.size() - 1; i++)
+ paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
+
+ if (useTransparencyLayer)
+ graphicsContext->endTransparencyLayer();
+}
+
+void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset,
+ const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline,
+ const Color outlineColor)
+{
+ RenderStyle* styleToUse = style();
+ LayoutUnit outlineWidth = styleToUse->outlineWidth();
+ EBorderStyle outlineStyle = styleToUse->outlineStyle();
+
+ bool antialias = shouldAntialiasLines(graphicsContext);
+
+ LayoutUnit offset = style()->outlineOffset();
+
+ LayoutUnit top = paintOffset.y() + thisline.y() - offset;
+ LayoutUnit left = paintOffset.x() + thisline.x() - offset;
+ LayoutUnit bottom = paintOffset.y() + thisline.maxY() + offset;
+ LayoutUnit right = paintOffset.x() + thisline.maxX() + offset;
+
+ // left edge
+ drawLineForBoxSide(graphicsContext,
+ left - outlineWidth,
+ top - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
+ left,
+ bottom + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
+ BSLeft,
+ outlineColor, outlineStyle,
+ (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
+ (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
+ antialias);
+
+ // right edge
+ drawLineForBoxSide(graphicsContext,
+ right,
+ top - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0),
+ right + outlineWidth,
+ bottom + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0),
+ BSRight,
+ outlineColor, outlineStyle,
+ (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth),
+ (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth),
+ antialias);
+ // upper edge
+ if (thisline.x() < lastline.x())
+ drawLineForBoxSide(graphicsContext,
+ left - outlineWidth,
+ top - outlineWidth,
+ min(right + outlineWidth, (lastline.isEmpty() ? 1000000 : paintOffset.x() + lastline.x())),
+ top,
+ BSTop, outlineColor, outlineStyle,
+ outlineWidth,
+ (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < right + outlineWidth) ? -outlineWidth : outlineWidth,
+ antialias);
+
+ if (lastline.maxX() < thisline.maxX())
+ drawLineForBoxSide(graphicsContext,
+ max(lastline.isEmpty() ? -1000000 : paintOffset.x() + lastline.maxX(), left - outlineWidth),
+ top - outlineWidth,
+ right + outlineWidth,
+ top,
+ BSTop, outlineColor, outlineStyle,
+ (!lastline.isEmpty() && left - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth,
+ outlineWidth, antialias);
+
+ if (thisline.x() == thisline.maxX())
+ drawLineForBoxSide(graphicsContext,
+ left - outlineWidth,
+ top - outlineWidth,
+ right + outlineWidth,
+ top,
+ BSTop, outlineColor, outlineStyle,
+ outlineWidth,
+ outlineWidth,
+ antialias);
+
+ // lower edge
+ if (thisline.x() < nextline.x())
+ drawLineForBoxSide(graphicsContext,
+ left - outlineWidth,
+ bottom,
+ min(right + outlineWidth, !nextline.isEmpty() ? paintOffset.x() + nextline.x() + 1 : 1000000),
+ bottom + outlineWidth,
+ BSBottom, outlineColor, outlineStyle,
+ outlineWidth,
+ (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < right + outlineWidth) ? -outlineWidth : outlineWidth,
+ antialias);
+
+ if (nextline.maxX() < thisline.maxX())
+ drawLineForBoxSide(graphicsContext,
+ max(!nextline.isEmpty() ? paintOffset.x() + nextline.maxX() : -1000000, left - outlineWidth),
+ bottom,
+ right + outlineWidth,
+ bottom + outlineWidth,
+ BSBottom, outlineColor, outlineStyle,
+ (!nextline.isEmpty() && left - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth,
+ outlineWidth, antialias);
+
+ if (thisline.x() == thisline.maxX())
+ drawLineForBoxSide(graphicsContext,
+ left - outlineWidth,
+ bottom,
+ right + outlineWidth,
+ bottom + outlineWidth,
+ BSBottom, outlineColor, outlineStyle,
+ outlineWidth,
+ outlineWidth,
+ antialias);
+}
+
+#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];
+
+ LayoutRect linesBoundingBox = this->linesBoundingBox();
+ LayoutUnit w = linesBoundingBox.width();
+ LayoutUnit h = linesBoundingBox.height();
+
+ DashboardRegionValue region;
+ region.label = styleRegion.label;
+ region.bounds = LayoutRect(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 (frame()) {
+ float deviceScaleFactor = frame()->page()->deviceScaleFactor();
+ if (deviceScaleFactor != 1.0f) {
+ region.bounds.scale(deviceScaleFactor);
+ region.clip.scale(deviceScaleFactor);
+ }
+ }
+
+ regions.append(region);
+ }
+}
+#endif
+
+} // namespace WebCore