#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);
bool childInline = newChild->isInline();
bool bcpInline = beforeChildParent->isInline();
bool flowInline = flow->isInline();
if (flow == beforeChildParent)
return flow->addChildToFlow(newChild, beforeChild);
else {
if (childInline == bcpInline)
return beforeChildParent->addChildToFlow(newChild, beforeChild);
else if (flowInline == childInline)
return flow->addChildToFlow(newChild, 0); 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()
{
if (m_continuation)
m_continuation->destroy();
m_continuation = 0;
RenderContainer::destroyLeftoverChildren();
if (!documentBeingDestroyed()) {
if (m_firstLineBox) {
if (isSelectionBorder())
view()->clearSelection();
if (m_firstLineBox->parent()) {
for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
box->remove();
}
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 (!firstLineBox()) {
if (isInline() && !parent()->selfNeedsLayout())
parent()->dirtyLinesFromChangedChild(this);
return;
}
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 (box) {
RootInlineBox* adjacentBox;
box->markDirty();
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)) return RenderContainer::createInlineBox(false, isRootLineBox);
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)
{
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);
if (!firstLineBox())
return;
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;
RenderView* v = view();
bool usePrintRect = !v->printRect().isEmpty();
for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
if (usePrintRect) {
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 (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);
if (!firstLineBox())
return false;
if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow()))
return false;
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()) {
ASSERT(!view() || !view()->layoutState() || isCompact() || isRunIn());
if (!firstLineBox() && !continuation())
return IntRect();
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();
}
int ow = style() ? style()->outlineSize() : 0;
if (isCompact())
left -= m_x;
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()) {
int x = r.x();
int y = r.y();
IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
cb->layer()->subtractScrollOffset(x, y); 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()) {
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()) {
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()) {
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)
{
if (firstChild() || style()->display() == INLINE)
return RenderContainer::caretRect(offset, affinity, extraWidthToEndOfLine);
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 {
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()) {
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;
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));
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));
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);
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
}