--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/RenderFlow.cpp Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 "RenderFlow.h"
+
+#include "Document.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "InlineTextBox.h"
+#include "RenderArena.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderView.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+#ifndef NDEBUG
+
+RenderFlow::~RenderFlow()
+{
+ ASSERT(!m_firstLineBox);
+ ASSERT(!m_lastLineBox);
+}
+
+#endif
+
+RenderFlow* RenderFlow::createAnonymousFlow(Document* doc, RenderStyle* style)
+{
+ RenderFlow* result;
+ if (style->display() == INLINE)
+ result = new (doc->renderArena()) RenderInline(doc);
+ else
+ result = new (doc->renderArena()) RenderBlock(doc);
+ result->setStyle(style);
+ return result;
+}
+
+RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
+{
+ if (beforeChild && beforeChild->parent() == this)
+ return this;
+
+ RenderFlow* curr = continuation();
+ RenderFlow* nextToLast = this;
+ RenderFlow* last = this;
+ while (curr) {
+ if (beforeChild && beforeChild->parent() == curr) {
+ if (curr->firstChild() == beforeChild)
+ return last;
+ return curr;
+ }
+
+ nextToLast = last;
+ last = curr;
+ curr = curr->continuation();
+ }
+
+ if (!beforeChild && !last->firstChild())
+ return nextToLast;
+ return last;
+}
+
+void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
+{
+ RenderFlow* flow = continuationBefore(beforeChild);
+ ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() ||
+ beforeChild->parent()->isRenderInline());
+ RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) :
+ (flow->continuation() ? flow->continuation() : flow);
+
+ if (newChild->isFloatingOrPositioned())
+ return beforeChildParent->addChildToFlow(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->addChildToFlow(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->addChildToFlow(newChild, beforeChild);
+ else if (flowInline == childInline)
+ return flow->addChildToFlow(newChild, 0); // Just treat like an append.
+ else
+ return beforeChildParent->addChildToFlow(newChild, beforeChild);
+ }
+}
+
+void RenderFlow::addChild(RenderObject* newChild, RenderObject* beforeChild)
+{
+ if (continuation())
+ return addChildWithContinuation(newChild, beforeChild);
+ return addChildToFlow(newChild, beforeChild);
+}
+
+void RenderFlow::extractLineBox(InlineFlowBox* box)
+{
+ checkConsistency();
+
+ m_lastLineBox = box->prevFlowBox();
+ if (box == m_firstLineBox)
+ m_firstLineBox = 0;
+ if (box->prevLineBox())
+ box->prevLineBox()->setNextLineBox(0);
+ box->setPreviousLineBox(0);
+ for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
+ curr->setExtracted();
+
+ checkConsistency();
+}
+
+void RenderFlow::attachLineBox(InlineFlowBox* box)
+{
+ checkConsistency();
+
+ if (m_lastLineBox) {
+ m_lastLineBox->setNextLineBox(box);
+ box->setPreviousLineBox(m_lastLineBox);
+ } else
+ m_firstLineBox = box;
+ InlineFlowBox* last = box;
+ for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) {
+ curr->setExtracted(false);
+ last = curr;
+ }
+ m_lastLineBox = last;
+
+ checkConsistency();
+}
+
+void RenderFlow::removeLineBox(InlineFlowBox* box)
+{
+ checkConsistency();
+
+ if (box == m_firstLineBox)
+ m_firstLineBox = box->nextFlowBox();
+ if (box == m_lastLineBox)
+ m_lastLineBox = box->prevFlowBox();
+ if (box->nextLineBox())
+ box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
+ if (box->prevLineBox())
+ box->prevLineBox()->setNextLineBox(box->nextLineBox());
+
+ checkConsistency();
+}
+
+void RenderFlow::deleteLineBoxes()
+{
+ if (m_firstLineBox) {
+ RenderArena* arena = renderArena();
+ InlineRunBox* next;
+ for (InlineRunBox* curr = m_firstLineBox; curr; curr = next) {
+ next = curr->nextLineBox();
+ curr->destroy(arena);
+ }
+ m_firstLineBox = 0;
+ m_lastLineBox = 0;
+ }
+}
+
+void RenderFlow::destroy()
+{
+ // Detach our continuation first.
+ if (m_continuation)
+ m_continuation->destroy();
+ m_continuation = 0;
+
+ // 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.
+ RenderContainer::destroyLeftoverChildren();
+
+ if (!documentBeingDestroyed()) {
+ if (m_firstLineBox) {
+ // We can't wait for RenderContainer::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 (m_firstLineBox->parent()) {
+ for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
+ box->remove();
+ }
+
+ // If we are an anonymous block, then our line boxes might have children
+ // that will outlast this block. In the non-anonymous block case those
+ // children will be destroyed by the time we return from this function.
+ if (isAnonymousBlock()) {
+ for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) {
+ while (InlineBox* childBox = box->firstChild())
+ childBox->remove();
+ }
+ }
+ } else if (isInline() && parent())
+ parent()->dirtyLinesFromChangedChild(this);
+ }
+
+ deleteLineBoxes();
+
+ RenderContainer::destroy();
+}
+
+void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child)
+{
+ if (!parent() || (selfNeedsLayout() && !isInlineFlow()) || isTable())
+ return;
+
+ // If we have no first line box, then just bail early.
+ if (!firstLineBox()) {
+ // For an empty inline, go ahead and propagate the check up to our parent, unless the parent
+ // is already dirty.
+ if (isInline() && !parent()->selfNeedsLayout())
+ parent()->dirtyLinesFromChangedChild(this);
+ return;
+ }
+
+ // Try to figure out which line box we belong in. First try to find a previous
+ // line box by examining our siblings. If we didn't find a line box, then use our
+ // parent's first line box.
+ RootInlineBox* box = 0;
+ RenderObject* curr = 0;
+ for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
+ if (curr->isFloatingOrPositioned())
+ continue;
+
+ if (curr->isReplaced()) {
+ InlineBox* wrapper = curr->inlineBoxWrapper();
+ if (wrapper)
+ box = wrapper->root();
+ } else if (curr->isText()) {
+ InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox();
+ if (textBox)
+ box = textBox->root();
+ } else if (curr->isInlineFlow()) {
+ InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox();
+ if (runBox)
+ box = runBox->root();
+ }
+
+ if (box)
+ break;
+ }
+ if (!box)
+ box = firstLineBox()->root();
+
+ // If we found a line box, then dirty it.
+ if (box) {
+ RootInlineBox* adjacentBox;
+ box->markDirty();
+
+ // dirty the adjacent lines that might be affected
+ // NOTE: we dirty the previous line because RootInlineBox objects cache
+ // the address of the first object on the next line after a BR, which we may be
+ // invalidating here. For more info, see how RenderBlock::layoutInlineChildren
+ // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak,
+ // despite the name, actually returns the first RenderObject after the BR.
+ // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize."
+ adjacentBox = box->prevRootBox();
+ if (adjacentBox)
+ adjacentBox->markDirty();
+ if (child->isBR() || (curr && curr->isBR())) {
+ adjacentBox = box->nextRootBox();
+ if (adjacentBox)
+ adjacentBox->markDirty();
+ }
+ }
+}
+
+short RenderFlow::lineHeight(bool firstLine, bool isRootLineBox) const
+{
+ if (firstLine) {
+ RenderStyle* s = style(firstLine);
+ Length lh = s->lineHeight();
+ if (lh.isNegative()) {
+ if (s == style()) {
+ if (m_lineHeight == -1)
+ m_lineHeight = RenderObject::lineHeight(false);
+ return m_lineHeight;
+ }
+ return s->font().lineSpacing();
+ }
+ if (lh.isPercent())
+ return lh.calcMinValue(s->fontSize());
+ return lh.value();
+ }
+
+ if (m_lineHeight == -1)
+ m_lineHeight = RenderObject::lineHeight(false);
+ return m_lineHeight;
+}
+
+void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox)
+{
+ if (!isRootLineBox && isReplaced())
+ return RenderContainer::dirtyLineBoxes(fullLayout, isRootLineBox);
+
+ if (fullLayout)
+ deleteLineBoxes();
+ else {
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
+ curr->dirtyLineBoxes();
+ }
+}
+
+InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun)
+{
+ checkConsistency();
+
+ if (!isRootLineBox &&
+ (isReplaced() || makePlaceHolderBox)) // Inline tables and inline blocks
+ return RenderContainer::createInlineBox(false, isRootLineBox); // (or positioned element placeholders).
+
+ InlineFlowBox* flowBox = 0;
+ if (isInlineFlow())
+ flowBox = new (renderArena()) InlineFlowBox(this);
+ else
+ flowBox = new (renderArena()) RootInlineBox(this);
+
+ if (!m_firstLineBox)
+ m_firstLineBox = m_lastLineBox = flowBox;
+ else {
+ m_lastLineBox->setNextLineBox(flowBox);
+ flowBox->setPreviousLineBox(m_lastLineBox);
+ m_lastLineBox = flowBox;
+ }
+
+ checkConsistency();
+
+ return flowBox;
+}
+
+void RenderFlow::paintLines(PaintInfo& paintInfo, int tx, int ty)
+{
+ // Only paint during the foreground/selection phases.
+ if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline
+ && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines)
+ return;
+
+ bool inlineFlow = isInlineFlow();
+ if (inlineFlow)
+ ASSERT(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer.
+
+ // If we have no lines then we have no work to do.
+ if (!firstLineBox())
+ return;
+
+ // We can check the first box and last box and avoid painting if we don't
+ // intersect. This is a quick short-circuit that we can take to avoid walking any lines.
+ // FIXME: This check is flawed in the following extremely obscure way:
+ // if some line in the middle has a huge overflow, it might actually extend below the last line.
+ int yPos = firstLineBox()->root()->topOverflow() - maximalOutlineSize(paintInfo.phase);
+ int h = maximalOutlineSize(paintInfo.phase) + lastLineBox()->root()->bottomOverflow() - yPos;
+ yPos += ty;
+ if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y())
+ return;
+
+ PaintInfo info(paintInfo);
+ RenderFlowSequencedSet outlineObjects;
+ info.outlineObjects = &outlineObjects;
+
+ // See if our root lines intersect with the dirty rect. If so, then we paint
+ // them. Note that boxes can easily overlap, so we can't make any assumptions
+ // based off positions of our first line box or our last line box.
+ RenderView* v = view();
+ bool usePrintRect = !v->printRect().isEmpty();
+ for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
+ if (usePrintRect) {
+ // FIXME: This is a feeble effort to avoid splitting a line across two pages.
+ // It is utterly inadequate, and this should not be done at paint time at all.
+ // The whole way objects break across pages needs to be redone.
+ // Try to avoid splitting a line vertically, but only if it's less than the height
+ // of the entire page.
+ if (curr->root()->bottomOverflow() - curr->root()->topOverflow() <= v->printRect().height()) {
+ if (ty + curr->root()->bottomOverflow() > v->printRect().bottom()) {
+ if (ty + curr->root()->topOverflow() < v->truncatedAt())
+ v->setBestTruncatedAt(ty + curr->root()->topOverflow(), this);
+ // If we were able to truncate, don't paint.
+ if (ty + curr->root()->topOverflow() >= v->truncatedAt())
+ break;
+ }
+ }
+ }
+
+ int top = min(curr->root()->topOverflow(), curr->root()->selectionTop()) - maximalOutlineSize(info.phase);
+ int bottom = curr->root()->bottomOverflow() + maximalOutlineSize(info.phase);
+ h = bottom - top;
+ yPos = ty + top;
+ if (yPos < info.rect.bottom() && yPos + h > info.rect.y())
+ curr->paint(info, tx, ty);
+ }
+
+ if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) {
+ RenderFlowSequencedSet::iterator end = info.outlineObjects->end();
+ for (RenderFlowSequencedSet::iterator it = info.outlineObjects->begin(); it != end; ++it) {
+ RenderFlow* flow = *it;
+ flow->paintOutline(info.context, tx, ty);
+ }
+ info.outlineObjects->clear();
+ }
+}
+
+bool RenderFlow::hitTestLines(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
+{
+ if (hitTestAction != HitTestForeground)
+ return false;
+
+ bool inlineFlow = isInlineFlow();
+ if (inlineFlow)
+ ASSERT(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer.
+
+ // If we have no lines then we have no work to do.
+ if (!firstLineBox())
+ return false;
+
+ // We can check the first box and last box and avoid hit testing if we don't
+ // contain the point. This is a quick short-circuit that we can take to avoid walking any lines.
+ // FIXME: This check is flawed in the following extremely obscure way:
+ // if some line in the middle has a huge overflow, it might actually extend below the last line.
+ if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow()))
+ return false;
+
+ // See if our root lines contain the point. If so, then we hit test
+ // them further. Note that boxes can easily overlap, so we can't make any assumptions
+ // based off positions of our first line box or our last line box.
+ for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
+ if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
+ bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty);
+ if (inside) {
+ updateHitTestResult(result, IntPoint(x - tx, y - ty));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+IntRect RenderFlow::absoluteClippedOverflowRect()
+{
+ if (isInlineFlow()) {
+ // Only compacts and run-ins are allowed in here during layout.
+ ASSERT(!view() || !view()->layoutState() || isCompact() || isRunIn());
+
+ if (!firstLineBox() && !continuation())
+ return IntRect();
+
+ // Find our leftmost position.
+ int left = 0;
+ int top = firstLineBox() ? firstLineBox()->yPos() : 0;
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ if (curr == firstLineBox() || curr->xPos() < left)
+ left = curr->xPos();
+ }
+
+ // Now invalidate a rectangle.
+ int ow = style() ? style()->outlineSize() : 0;
+ if (isCompact())
+ left -= m_x;
+
+ // 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->isInlineFlow() && inlineFlow != cb;
+ inlineFlow = inlineFlow->parent()) {
+ if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
+ inlineFlow->layer()->relativePositionOffset(left, top);
+ }
+
+ IntRect r(-ow + left, -ow + top, width() + ow * 2, 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()->subtractScrollOffset(x, y); // For overflow:auto/scroll/hidden.
+ IntRect repaintRect(x, y, r.width(), r.height());
+ r = intersection(repaintRect, boxRect);
+ }
+ cb->computeAbsoluteRepaintRect(r);
+
+ if (ow) {
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
+ if (!curr->isText()) {
+ IntRect childRect = curr->getAbsoluteRepaintRectWithOutline(ow);
+ r.unite(childRect);
+ }
+ }
+
+ if (continuation() && !continuation()->isInline()) {
+ IntRect contRect = continuation()->getAbsoluteRepaintRectWithOutline(ow);
+ r.unite(contRect);
+ }
+ }
+
+ return r;
+ }
+
+ return RenderContainer::absoluteClippedOverflowRect();
+}
+
+int RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ ASSERT(!isInlineFlow());
+ if (!includeOverflowInterior && hasOverflowClip())
+ return includeSelf && m_width > 0 ? overflowHeight(false) : 0;
+
+ int bottom = includeSelf && m_width > 0 ? m_height : 0;
+ if (!hasColumns()) {
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject* c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow())
+ bottom = max(bottom, c->yPos() + c->lowestPosition(false));
+ }
+ }
+
+ if (includeSelf && isRelPositioned())
+ bottom += relativePositionOffsetY();
+
+ return bottom;
+}
+
+int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ ASSERT(!isInlineFlow());
+ if (!includeOverflowInterior && hasOverflowClip())
+ return includeSelf && m_height > 0 ? overflowWidth(false) : 0;
+
+ int right = includeSelf && m_height > 0 ? m_width : 0;
+ if (!hasColumns()) {
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject* c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow())
+ right = max(right, c->xPos() + c->rightmostPosition(false));
+ }
+ }
+
+ if (includeSelf && isRelPositioned())
+ right += relativePositionOffsetX();
+
+ return right;
+}
+
+int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+ ASSERT(!isInlineFlow());
+ if (!includeOverflowInterior && hasOverflowClip())
+ return includeSelf && m_height > 0 ? overflowLeft(false) : m_width;
+
+ int left = includeSelf && m_height > 0 ? 0 : m_width;
+ if (!hasColumns()) {
+ // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+ // For now, we have to descend into all the children, since we may have a huge abs div inside
+ // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
+ // the abs div.
+ for (RenderObject* c = firstChild(); c; c = c->nextSibling()) {
+ if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow())
+ left = min(left, c->xPos() + c->leftmostPosition(false));
+ }
+ }
+
+ if (includeSelf && isRelPositioned())
+ left += relativePositionOffsetX();
+
+ return left;
+}
+
+IntRect RenderFlow::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
+{
+ // Do the normal calculation in most cases.
+ if (firstChild() || style()->display() == INLINE)
+ return RenderContainer::caretRect(offset, affinity, extraWidthToEndOfLine);
+
+ // This is a special case:
+ // The element is not an inline element, and it's empty. So we have to
+ // calculate a fake position to indicate where objects are to be inserted.
+
+ // FIXME: This does not take into account either :first-line or :first-letter
+ // However, as soon as some content is entered, the line boxes will be
+ // constructed and this kludge is not called any more. So only the caret size
+ // of an empty :first-line'd block is wrong. I think we can live with that.
+ RenderStyle* currentStyle = firstLineStyle();
+ int height = lineHeight(true);
+ const int caretWidth = 1;
+
+ enum CaretAlignment { alignLeft, alignRight, alignCenter };
+
+ CaretAlignment alignment = alignLeft;
+
+ switch (currentStyle->textAlign()) {
+ case TAAUTO:
+ case JUSTIFY:
+ if (currentStyle->direction() == RTL)
+ alignment = alignRight;
+ break;
+ case LEFT:
+ case WEBKIT_LEFT:
+ break;
+ case CENTER:
+ case WEBKIT_CENTER:
+ alignment = alignCenter;
+ break;
+ case RIGHT:
+ case WEBKIT_RIGHT:
+ alignment = alignRight;
+ break;
+ }
+
+ int x = borderLeft() + paddingLeft();
+ int w = width();
+
+ switch (alignment) {
+ case alignLeft:
+ break;
+ case alignCenter:
+ x = (x + w - (borderRight() + paddingRight())) / 2;
+ break;
+ case alignRight:
+ x = w - (borderRight() + paddingRight());
+ break;
+ }
+
+ if (extraWidthToEndOfLine) {
+ if (isRenderBlock()) {
+ *extraWidthToEndOfLine = w - (x + caretWidth);
+ } else {
+ // FIXME: This code looks wrong.
+ // myRight and containerRight are set up, but then clobbered.
+ // So *extraWidthToEndOfLine will always be 0 here.
+
+ int myRight = x + caretWidth;
+ int ignore;
+ absolutePositionForContent(myRight, ignore);
+
+ int containerRight = containingBlock()->xPos() + containingBlockWidth();
+ absolutePositionForContent(containerRight, ignore);
+
+ *extraWidthToEndOfLine = containerRight - myRight;
+ }
+ }
+
+ int absx, absy;
+ absolutePositionForContent(absx, absy);
+ x += absx;
+ int y = absy + paddingTop() + borderTop();
+
+ return IntRect(x, y, caretWidth, height);
+}
+
+void RenderFlow::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty)
+{
+ if (isRenderBlock()) {
+ // Continuations should include their margins in the outline rect.
+ if (continuation()) {
+ bool nextInlineHasLineBox = continuation()->firstLineBox();
+ bool prevInlineHasLineBox = static_cast<RenderFlow*>(continuation()->element()->renderer())->firstLineBox();
+ int topMargin = prevInlineHasLineBox ? collapsedMarginTop() : 0;
+ int bottomMargin = nextInlineHasLineBox ? collapsedMarginBottom() : 0;
+ graphicsContext->addFocusRingRect(IntRect(tx, ty - topMargin,
+ width(), height() + topMargin + bottomMargin));
+ } else
+ graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height()));
+ }
+
+ if (!hasOverflowClip() && !hasControlClip()) {
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
+ graphicsContext->addFocusRingRect(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height()));
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ if (!curr->isText() && !curr->isListMarker()) {
+ int x = 0;
+ int y = 0;
+ if (curr->layer())
+ curr->absolutePosition(x, y);
+ else {
+ x = tx + curr->xPos();
+ y = ty + curr->yPos();
+ }
+ curr->addFocusRingRects(graphicsContext, x, y);
+ }
+ }
+
+ if (continuation()) {
+ if (isInline())
+ continuation()->addFocusRingRects(graphicsContext,
+ tx - containingBlock()->xPos() + continuation()->xPos(),
+ ty - containingBlock()->yPos() + continuation()->yPos());
+ else
+ continuation()->addFocusRingRects(graphicsContext,
+ tx - xPos() + continuation()->containingBlock()->xPos(),
+ ty - yPos() + continuation()->containingBlock()->yPos());
+ }
+}
+
+void RenderFlow::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() <= BHIDDEN)
+ return;
+
+ Vector<IntRect*> rects;
+
+ rects.append(new IntRect);
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
+ rects.append(new IntRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()));
+
+ rects.append(new 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));
+ deleteAllValues(rects);
+}
+
+void RenderFlow::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
+ drawBorder(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
+ drawBorder(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())
+ drawBorder(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())
+ drawBorder(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())
+ drawBorder(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())
+ drawBorder(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);
+}
+
+#ifndef NDEBUG
+
+void RenderFlow::checkConsistency() const
+{
+#ifdef CHECK_CONSISTENCY
+ const InlineFlowBox* prev = 0;
+ for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextFlowBox()) {
+ ASSERT(child->object() == this);
+ ASSERT(child->prevFlowBox() == prev);
+ prev = child;
+ }
+ ASSERT(prev == m_lastLineBox);
+#endif
+}
+
+#endif
+
+} // namespace WebCore