summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderBlock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/RenderBlock.cpp')
-rw-r--r--WebCore/rendering/RenderBlock.cpp891
1 files changed, 614 insertions, 277 deletions
diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp
index 52f5c52..0e44b32 100644
--- a/WebCore/rendering/RenderBlock.cpp
+++ b/WebCore/rendering/RenderBlock.cpp
@@ -118,7 +118,7 @@ RenderBlock::RenderBlock(Node* node)
, m_floatingObjects(0)
, m_positionedObjects(0)
, m_continuation(0)
- , m_maxMargin(0)
+ , m_rareData(0)
, m_lineHeight(-1)
{
setChildrenInline(true);
@@ -128,7 +128,6 @@ RenderBlock::~RenderBlock()
{
delete m_floatingObjects;
delete m_positionedObjects;
- delete m_maxMargin;
if (hasColumns())
delete gColumnInfoMap->take(this);
@@ -1116,7 +1115,7 @@ void RenderBlock::layout()
clearLayoutOverflow();
}
-void RenderBlock::layoutBlock(bool relayoutChildren)
+void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight)
{
ASSERT(needsLayout());
@@ -1127,7 +1126,6 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
return;
LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
- LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection());
int oldWidth = width();
int oldColumnWidth = desiredColumnWidth();
@@ -1148,6 +1146,30 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
int previousHeight = height();
setHeight(0);
+ bool hasSpecifiedPageHeight = false;
+ ColumnInfo* colInfo = columnInfo();
+ if (hasColumns()) {
+ if (!pageHeight) {
+ // We need to go ahead and set our explicit page height if one exists, so that we can
+ // avoid doing two layout passes.
+ calcHeight();
+ int columnHeight = contentHeight();
+ if (columnHeight > 0) {
+ pageHeight = columnHeight;
+ hasSpecifiedPageHeight = true;
+ }
+ setHeight(0);
+ }
+ if (colInfo->columnHeight() != pageHeight && m_everHadLayout) {
+ colInfo->setColumnHeight(pageHeight);
+ markDescendantBlocksAndLinesForLayout(); // We need to dirty all descendant blocks and lines, since the column height is different now.
+ }
+
+ if (!hasSpecifiedPageHeight && !pageHeight)
+ colInfo->clearForcedBreaks();
+ }
+
+ LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection(), pageHeight, colInfo);
// We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track
// our current maximal positive and negative margins. These values are used when we
@@ -1162,7 +1184,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
bool isCell = isTableCell();
if (!isCell) {
initMaxMarginValues();
-
+
setTopMarginQuirk(style()->marginTop().quirk());
setBottomMarginQuirk(style()->marginBottom().quirk());
@@ -1172,6 +1194,8 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
// a bottom margin.
setMaxBottomMargins(0, 0);
}
+
+ setPaginationStrut(0);
}
// For overflow:scroll blocks, ensure we have both scrollbars in place always.
@@ -1197,10 +1221,9 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
if (floatBottom() > (height() - toAdd) && expandsToEncloseOverhangingFloats())
setHeight(floatBottom() + toAdd);
- // Now lay out our columns within this intrinsic height, since they can slightly affect the intrinsic height as
- // we adjust for clean column breaks.
- int singleColumnBottom = layoutColumns();
-
+ if (layoutColumns(hasSpecifiedPageHeight, pageHeight, statePusher))
+ return;
+
// Calculate our new height.
int oldHeight = height();
calcHeight();
@@ -1215,21 +1238,18 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
}
}
}
-
- // We have to rebalance columns to the new height.
- layoutColumns(singleColumnBottom);
}
if (previousHeight != height())
relayoutChildren = true;
- // This check is designed to catch anyone
- // who wasn't going to propagate float information up to the parent and yet could potentially be painted by its ancestor.
- if (isRoot() || expandsToEncloseOverhangingFloats())
- addOverflowFromFloats();
-
// Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
if (!hasColumns()) {
+ // This check is designed to catch anyone
+ // who wasn't going to propagate float information up to the parent and yet could potentially be painted by its ancestor.
+ if (isRoot() || expandsToEncloseOverhangingFloats())
+ addOverflowFromFloats();
+
if (childrenInline())
addOverflowFromInlineChildren();
else
@@ -1245,6 +1265,9 @@ void RenderBlock::layoutBlock(bool relayoutChildren)
statePusher.pop();
+ if (view()->layoutState()->m_pageHeight)
+ setPageY(view()->layoutState()->pageY(y()));
+
// Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
// we overflow or not.
updateScrollInfoAfterLayout();
@@ -1493,7 +1516,8 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo)
if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop))
marginInfo.setTopQuirk(topQuirk);
- int ypos = height();
+ int beforeCollapseY = height();
+ int ypos = beforeCollapseY;
if (child->isSelfCollapsingBlock()) {
// This child has no height. We need to compute our
// position before we collapse the child's margins together,
@@ -1535,6 +1559,14 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo)
marginInfo.setBottomQuirk(child->isBottomMarginQuirk() || style()->marginBottomCollapse() == MDISCARD);
}
+ // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins
+ // collapsed into the page edge.
+ bool paginated = view()->layoutState()->isPaginated();
+ if (paginated && ypos > beforeCollapseY) {
+ int oldY = ypos;
+ ypos = min(ypos, nextPageTop(beforeCollapseY));
+ setHeight(height() + (ypos - oldY));
+ }
return ypos;
}
@@ -1594,7 +1626,27 @@ int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& ma
int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop();
yPosEstimate += max(marginInfo.margin(), childMarginTop);
}
+
+ bool paginated = view()->layoutState()->isPaginated();
+
+ // Adjust yPosEstimate down to the next page if the margins are so large that we don't fit on the current
+ // page.
+ if (paginated && yPosEstimate > height())
+ yPosEstimate = min(yPosEstimate, nextPageTop(height()));
+
yPosEstimate += getClearDelta(child, yPosEstimate);
+
+ if (paginated) {
+ // If the object has a page or column break value of "before", then we should shift to the top of the next page.
+ yPosEstimate = applyBeforeBreak(child, yPosEstimate);
+
+ // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
+ yPosEstimate = adjustForUnsplittableChild(child, yPosEstimate);
+
+ if (!child->selfNeedsLayout() && child->isRenderBlock())
+ yPosEstimate += toRenderBlock(child)->paginationStrut();
+ }
+
return yPosEstimate;
}
@@ -1784,8 +1836,9 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
view()->addLayoutDelta(IntSize(0, child->y() - yPosEstimate));
child->setLocation(child->x(), yPosEstimate);
+ RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0;
bool markDescendantsWithFloats = false;
- if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats())
+ if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats())
markDescendantsWithFloats = true;
else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) {
// If an element might be affected by the presence of floats, then always mark it for
@@ -1795,18 +1848,24 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
markDescendantsWithFloats = true;
}
- if (child->isRenderBlock()) {
+ if (childRenderBlock) {
if (markDescendantsWithFloats)
- toRenderBlock(child)->markAllDescendantsWithFloatsForLayout();
-
+ childRenderBlock->markAllDescendantsWithFloatsForLayout();
previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom());
}
+ bool paginated = view()->layoutState()->isPaginated();
+ if (!child->needsLayout() && paginated && view()->layoutState()->m_pageHeight && childRenderBlock && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY())
+ childRenderBlock->markForPaginationRelayout();
+
bool childHadLayout = child->m_everHadLayout;
bool childNeededLayout = child->needsLayout();
if (childNeededLayout)
child->layout();
+ // Cache if we are at the top of the block right now.
+ bool atTopOfBlock = marginInfo.atTopOfBlock();
+
// Now determine the correct ypos based off examination of collapsing margin
// values.
int yBeforeClear = collapseMargins(child, marginInfo);
@@ -1814,6 +1873,41 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
// Now check for clear.
int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear);
+ if (paginated) {
+ int oldY = yAfterClear;
+
+ // If the object has a page or column break value of "before", then we should shift to the top of the next page.
+ yAfterClear = applyBeforeBreak(child, yAfterClear);
+
+ // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
+ int yBeforeUnsplittableAdjustment = yAfterClear;
+ int yAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, yAfterClear);
+
+ int paginationStrut = 0;
+ int unsplittableAdjustmentDelta = yAfterUnsplittableAdjustment - yBeforeUnsplittableAdjustment;
+ if (unsplittableAdjustmentDelta)
+ paginationStrut = unsplittableAdjustmentDelta;
+ else if (childRenderBlock && childRenderBlock->paginationStrut())
+ paginationStrut = childRenderBlock->paginationStrut();
+
+ if (paginationStrut) {
+ // We are willing to propagate out to our parent block as long as we were at the top of the block prior
+ // to collapsing our margins, and as long as we didn't clear or move as a result of other pagination.
+ if (atTopOfBlock && oldY == yBeforeClear && !isPositioned() && !isTableCell()) {
+ // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't
+ // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too
+ // and pushes to the next page anyway, so not too concerned about it.
+ setPaginationStrut(yAfterClear + paginationStrut);
+ if (childRenderBlock)
+ childRenderBlock->setPaginationStrut(0);
+ } else
+ yAfterClear += paginationStrut;
+ }
+
+ // Similar to how we apply clearance. Go ahead and boost height() to be the place where we're going to position the child.
+ setHeight(height() + (yAfterClear - oldY));
+ }
+
view()->addLayoutDelta(IntSize(0, yPosEstimate - yAfterClear));
child->setLocation(child->x(), yAfterClear);
@@ -1826,8 +1920,13 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
// So go ahead and mark the item as dirty.
child->setChildNeedsLayout(true, false);
}
- if (!child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats())
- toRenderBlock(child)->markAllDescendantsWithFloatsForLayout();
+ if (childRenderBlock) {
+ if (!child->avoidsFloats() && childRenderBlock->containsFloats())
+ childRenderBlock->markAllDescendantsWithFloatsForLayout();
+ if (paginated && !child->needsLayout() && view()->layoutState()->m_pageHeight && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY())
+ childRenderBlock->markForPaginationRelayout();
+ }
+
// Our guess was wrong. Make the child lay itself out again.
child->layoutIfNeeded();
}
@@ -1848,7 +1947,7 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
}
// If the child has overhanging floats that intrude into following siblings (or possibly out
// of this block), then the parent gets notified of the floats now.
- if (child->isBlockFlow() && toRenderBlock(child)->containsFloats())
+ if (childRenderBlock && childRenderBlock->containsFloats())
maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout));
IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y());
@@ -1867,6 +1966,13 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int
child->repaintOverhangingFloats(true);
}
+ if (paginated) {
+ // Check for an after page/column break.
+ int newHeight = applyAfterBreak(child, height(), marginInfo);
+ if (newHeight != height())
+ setHeight(newHeight);
+ }
+
ASSERT(oldLayoutDelta == view()->layoutDelta());
}
@@ -1904,6 +2010,11 @@ bool RenderBlock::layoutOnlyPositionedObjects()
void RenderBlock::layoutPositionedObjects(bool relayoutChildren)
{
if (m_positionedObjects) {
+ if (hasColumns())
+ view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns.
+
+ bool paginated = view()->layoutState()->isPaginated();
+
RenderBox* r;
Iterator end = m_positionedObjects->end();
for (Iterator it = m_positionedObjects->begin(); it != end; ++it) {
@@ -1919,12 +2030,21 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren)
//if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent()))
r->setPrefWidthsDirty(true, false);
+ if (!r->needsLayout() && paginated && view()->layoutState()->m_pageHeight) {
+ RenderBlock* childRenderBlock = r->isRenderBlock() ? toRenderBlock(r) : 0;
+ if (childRenderBlock && view()->layoutState()->pageY(childRenderBlock->y()) != childRenderBlock->pageY())
+ childRenderBlock->markForPaginationRelayout();
+ }
+
// We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width
// and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout.
if (r->needsPositionedMovementLayoutOnly())
r->tryLayoutDoingPositionedMovementOnly();
r->layoutIfNeeded();
}
+
+ if (hasColumns())
+ view()->layoutState()->m_columnInfo = columnInfo(); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work.
}
}
@@ -2012,12 +2132,12 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty)
// We need to do multiple passes, breaking up our child painting into strips.
ColumnInfo* colInfo = columnInfo();
- unsigned colCount = colInfo->columnCount();
+ unsigned colCount = columnCount(colInfo);
int currXOffset = style()->direction() == LTR ? 0 : contentWidth();
int ruleAdd = borderLeft() + paddingLeft();
int ruleX = style()->direction() == LTR ? 0 : contentWidth();
for (unsigned i = 0; i < colCount; i++) {
- IntRect colRect = colInfo->columnRectAt(i);
+ IntRect colRect = columnRectAt(colInfo, i);
// Move to the next position.
if (style()->direction() == LTR) {
@@ -2048,14 +2168,14 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool
GraphicsContext* context = paintInfo.context;
int colGap = columnGap();
ColumnInfo* colInfo = columnInfo();
- unsigned colCount = colInfo->columnCount();
+ unsigned colCount = columnCount(colInfo);
if (!colCount)
return;
- int currXOffset = style()->direction() == LTR ? 0 : contentWidth() - colInfo->columnRectAt(0).width();
+ int currXOffset = style()->direction() == LTR ? 0 : contentWidth() - columnRectAt(colInfo, 0).width();
int currYOffset = 0;
for (unsigned i = 0; i < colCount; i++) {
// For each rect, we clip to the rect, and then we adjust our coords.
- IntRect colRect = colInfo->columnRectAt(i);
+ IntRect colRect = columnRectAt(colInfo, i);
colRect.move(tx, ty);
PaintInfo info(paintInfo);
info.rect.intersect(colRect);
@@ -2112,15 +2232,14 @@ void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty)
info.phase = newPhase;
info.updatePaintingRootForChildren(this);
+ // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit
+ // NSViews. Do not add any more code for this.
RenderView* renderView = view();
- bool usePrintRect = !renderView->printRect().isEmpty() && !document()->settings()->paginateDuringLayoutEnabled();
+ bool usePrintRect = !renderView->printRect().isEmpty();
- bool checkPageBreaks = document()->paginated() && !document()->settings()->paginateDuringLayoutEnabled();
- bool checkColumnBreaks = !checkPageBreaks && usePrintRect;
-
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
// Check for page-break-before: always, and if it's set, break and bail.
- bool checkBeforeAlways = !childrenInline() && ((checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS) || (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS));
+ bool checkBeforeAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakBefore() == PBALWAYS);
if (checkBeforeAlways
&& (ty + child->y()) > paintInfo.rect.y()
&& (ty + child->y()) < paintInfo.rect.bottom()) {
@@ -2143,7 +2262,7 @@ void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty)
child->paint(info, tx, ty);
// Check for page-break-after: always, and if it's set, break and bail.
- bool checkAfterAlways = !childrenInline() && ((checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS) || (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS));
+ bool checkAfterAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakAfter() == PBALWAYS);
if (checkAfterAlways
&& (ty + child->y() + child->height()) > paintInfo.rect.y()
&& (ty + child->y() + child->height()) < paintInfo.rect.bottom()) {
@@ -2155,7 +2274,7 @@ void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty)
void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType type)
{
- SelectionController* selection = type == CursorCaret ? frame()->selection() : frame()->dragCaretController();
+ SelectionController* selection = type == CursorCaret ? frame()->selection() : frame()->page()->dragCaretController();
// Paint the caret if the SelectionController says so or if caret browsing is enabled
bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled();
@@ -2168,7 +2287,7 @@ void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType typ
if (type == CursorCaret)
frame()->selection()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect);
else
- frame()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect);
+ frame()->selection()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect);
}
}
@@ -2791,7 +2910,7 @@ void RenderBlock::removePositionedObjects(RenderBlock* o)
m_positionedObjects->remove(deadObjects.at(i));
}
-void RenderBlock::insertFloatingObject(RenderBox* o)
+RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o)
{
ASSERT(o->isFloating());
@@ -2804,25 +2923,37 @@ void RenderBlock::insertFloatingObject(RenderBox* o)
DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects);
FloatingObject* f;
while ( (f = it.current()) ) {
- if (f->m_renderer == o) return;
+ if (f->m_renderer == o)
+ return f;
++it;
}
}
// Create the special object entry & append it to the list
- o->layoutIfNeeded();
-
FloatingObject* newObj = new FloatingObject(o->style()->floating() == FLEFT ? FloatingObject::FloatLeft : FloatingObject::FloatRight);
newObj->m_top = -1;
newObj->m_bottom = -1;
+
+ // Our location is irrelevant if we're unsplittable or no pagination is in effect.
+ // Just go ahead and lay out the float.
+ bool affectedByPagination = o->isRenderBlock() && view()->layoutState()->m_pageHeight;
+ if (!affectedByPagination)
+ o->layoutIfNeeded();
+ else {
+ o->calcWidth();
+ o->calcVerticalMargins();
+ }
newObj->m_width = o->width() + o->marginLeft() + o->marginRight();
+
newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will.
newObj->m_isDescendant = true;
newObj->m_renderer = o;
m_floatingObjects->append(newObj);
+
+ return newObj;
}
void RenderBlock::removeFloatingObject(RenderBox* o)
@@ -2846,6 +2977,18 @@ void RenderBlock::removeFloatingObject(RenderBox* o)
}
}
+void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int y)
+{
+ if (!m_floatingObjects)
+ return;
+
+ FloatingObject* curr = m_floatingObjects->last();
+ while (curr != lastFloat && (curr->m_top == -1 || curr->m_top >= y)) {
+ m_floatingObjects->removeLast();
+ curr = m_floatingObjects->last();
+ }
+}
+
bool RenderBlock::positionNewFloats()
{
if (!m_floatingObjects)
@@ -2882,16 +3025,15 @@ bool RenderBlock::positionNewFloats()
}
RenderBox* o = f->m_renderer;
- int _height = o->height() + o->marginTop() + o->marginBottom();
int ro = rightOffset(); // Constant part of right offset.
- int lo = leftOffset(); // Constat part of left offset.
+ int lo = leftOffset(); // Constant part of left offset.
int fwidth = f->m_width; // The width we look for.
if (ro - lo < fwidth)
fwidth = ro - lo; // Never look for more than what will be available.
IntRect oldRect(o->x(), o->y() , o->width(), o->height());
-
+
if (o->style()->clear() & CLEFT)
y = max(leftBottom(), y);
if (o->style()->clear() & CRIGHT)
@@ -2920,9 +3062,38 @@ bool RenderBlock::positionNewFloats()
o->setLocation(fx - o->marginRight() - o->width(), y + o->marginTop());
}
- f->m_top = y;
- f->m_bottom = f->m_top + _height;
+ if (view()->layoutState()->isPaginated()) {
+ RenderBlock* childBlock = o->isRenderBlock() ? toRenderBlock(o) : 0;
+ if (childBlock && view()->layoutState()->m_pageHeight && view()->layoutState()->pageY(o->y()) != childBlock->pageY())
+ childBlock->markForPaginationRelayout();
+ o->layoutIfNeeded();
+
+ // If we are unsplittable and don't fit, then we need to move down.
+ // We include our margins as part of the unsplittable area.
+ int newY = adjustForUnsplittableChild(o, y, true);
+
+ // See if we have a pagination strut that is making us move down further.
+ // Note that an unsplittable child can't also have a pagination strut, so this is
+ // exclusive with the case above.
+ if (childBlock && childBlock->paginationStrut()) {
+ newY += childBlock->paginationStrut();
+ childBlock->setPaginationStrut(0);
+ }
+
+ if (newY != y) {
+ f->m_paginationStrut = newY - y;
+ y = newY;
+ o->setY(y + o->marginTop());
+ if (childBlock)
+ childBlock->setChildNeedsLayout(true, false);
+ o->layoutIfNeeded();
+ }
+ }
+
+ f->m_top = y;
+ f->m_bottom = f->m_top + o->marginTop() + o->height() + o->marginBottom();
+
// If the child moved, we have to repaint it.
if (o->checkForRepaintDuringLayout())
o->repaintDuringLayoutIfMoved(oldRect);
@@ -2932,6 +3103,40 @@ bool RenderBlock::positionNewFloats()
return true;
}
+bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine)
+{
+ bool didPosition = positionNewFloats();
+ if (!didPosition || !newFloat->m_paginationStrut)
+ return didPosition;
+
+ int floatTop = newFloat->m_top;
+ int paginationStrut = newFloat->m_paginationStrut;
+ FloatingObject* f = m_floatingObjects->last();
+
+ ASSERT(f == newFloat);
+
+ if (floatTop - paginationStrut != height())
+ return didPosition;
+
+ for (f = m_floatingObjects->prev(); f && f != lastFloatFromPreviousLine; f = m_floatingObjects->prev()) {
+ if (f->m_top == height()) {
+ ASSERT(!f->m_paginationStrut);
+ f->m_paginationStrut = paginationStrut;
+ RenderBox* o = f->m_renderer;
+ o->setY(o->y() + o->marginTop() + paginationStrut);
+ if (o->isRenderBlock())
+ toRenderBlock(o)->setChildNeedsLayout(true, false);
+ o->layoutIfNeeded();
+ f->m_top += f->m_paginationStrut;
+ f->m_bottom += f->m_paginationStrut;
+ }
+ }
+
+ setHeight(height() + paginationStrut);
+
+ return didPosition;
+}
+
void RenderBlock::newLine(EClear clear)
{
positionNewFloats();
@@ -3173,8 +3378,8 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf)
if (hasColumns()) {
ColumnInfo* colInfo = columnInfo();
- for (unsigned i = 0; i < colInfo->columnCount(); i++)
- bottom = max(bottom, colInfo->columnRectAt(i).bottom() + relativeOffset);
+ for (unsigned i = 0; i < columnCount(colInfo); i++)
+ bottom = max(bottom, columnRectAt(colInfo, i).bottom() + relativeOffset);
return bottom;
}
@@ -3268,8 +3473,9 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel
// This only matters for LTR
if (style()->direction() == LTR) {
ColumnInfo* colInfo = columnInfo();
- if (colInfo->columnCount())
- right = max(colInfo->columnRectAt(colInfo->columnCount() - 1).right() + relativeOffset, right);
+ unsigned count = columnCount(colInfo);
+ if (count)
+ right = max(columnRectAt(colInfo, count - 1).right() + relativeOffset, right);
}
return right;
}
@@ -3368,8 +3574,9 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf
// This only matters for RTL
if (style()->direction() == RTL) {
ColumnInfo* colInfo = columnInfo();
- if (colInfo->columnCount())
- left = min(colInfo->columnRectAt(colInfo->columnCount() - 1).x() + relativeOffset, left);
+ unsigned count = columnCount(colInfo);
+ if (count)
+ left = min(columnRectAt(colInfo, count - 1).x() + relativeOffset, left);
}
return left;
}
@@ -3662,6 +3869,9 @@ bool RenderBlock::containsFloat(RenderObject* o)
void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout)
{
+ if (!m_everHadLayout)
+ return;
+
setChildNeedsLayout(true, !inLayout);
if (floatToRemove)
@@ -3679,30 +3889,39 @@ void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove
}
}
-int RenderBlock::visibleTopOfHighestFloatExtendingBelow(int bottom, int maxHeight) const
+void RenderBlock::markDescendantBlocksAndLinesForLayout(bool inLayout)
{
- int top = bottom;
- if (m_floatingObjects) {
- FloatingObject* floatingObject;
- for (DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); (floatingObject = it.current()); ++it) {
- RenderBox* floatingBox = floatingObject->m_renderer;
- IntRect visibleOverflow = floatingBox->visibleOverflowRect();
- visibleOverflow.move(floatingBox->x(), floatingBox->y());
- if (visibleOverflow.y() < top && visibleOverflow.bottom() > bottom && visibleOverflow.height() <= maxHeight && floatingBox->containingBlock() == this)
- top = visibleOverflow.y();
- }
- }
+ if (!m_everHadLayout)
+ return;
+
+ setChildNeedsLayout(true, !inLayout);
+ // Iterate over our children and mark them as needed.
if (!childrenInline()) {
- for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
- if (child->isFloatingOrPositioned() || !child->isRenderBlock())
+ for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+ if (child->isFloatingOrPositioned())
continue;
- RenderBlock* childBlock = toRenderBlock(child);
- top = min(top, childBlock->y() + childBlock->visibleTopOfHighestFloatExtendingBelow(bottom - childBlock->y(), maxHeight));
+ child->markDescendantBlocksAndLinesForLayout(inLayout);
+ }
+ }
+
+ // Walk our floating objects and mark them too.
+ if (m_floatingObjects) {
+ DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects);
+ while (it.current()) {
+ if (it.current()->m_renderer->isRenderBlock())
+ it.current()->m_renderer->markDescendantBlocksAndLinesForLayout(inLayout);
+ ++it;
}
}
- return top;
+ if (m_positionedObjects) {
+ // FIXME: Technically we don't have to mark the positioned objects if we're the block
+ // that established the columns, but we don't really have that information here.
+ Iterator end = m_positionedObjects->end();
+ for (Iterator it = m_positionedObjects->begin(); it != end; ++it)
+ (*it)->markDescendantBlocksAndLinesForLayout();
+ }
}
int RenderBlock::getClearDelta(RenderBox* child, int yPos)
@@ -3862,16 +4081,16 @@ bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& r
{
// We need to do multiple passes, breaking up our hit testing into strips.
ColumnInfo* colInfo = columnInfo();
- int colCount = colInfo->columnCount();
+ int colCount = columnCount(colInfo);
if (!colCount)
return false;
int left = borderLeft() + paddingLeft();
int currYOffset = 0;
int i;
for (i = 0; i < colCount; i++)
- currYOffset -= colInfo->columnRectAt(i).height();
+ currYOffset -= columnRectAt(colInfo, i).height();
for (i = colCount - 1; i >= 0; i--) {
- IntRect colRect = colInfo->columnRectAt(i);
+ IntRect colRect = columnRectAt(colInfo, i);
int currXOffset = colRect.x() - left;
currYOffset += colRect.height();
colRect.move(tx, ty);
@@ -4101,7 +4320,15 @@ int RenderBlock::availableWidth() const
// If we have multiple columns, then the available width is reduced to our column width.
if (hasColumns())
return desiredColumnWidth();
- return contentWidth();
+ return RenderBox::availableWidth();
+}
+
+int RenderBlock::availableLogicalWidth() const
+{
+ // If we have multiple columns, then the available logical width is reduced to our column width.
+ if (hasColumns())
+ return desiredColumnWidth();
+ return RenderBox::availableLogicalWidth();
}
int RenderBlock::columnGap() const
@@ -4165,8 +4392,7 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, int width)
bool destroyColumns = !firstChild()
|| (count == 1 && style()->hasAutoColumnWidth())
|| firstChild()->isAnonymousColumnsBlock()
- || firstChild()->isAnonymousColumnSpanBlock()
- || document()->settings()->paginateDuringLayoutEnabled();
+ || firstChild()->isAnonymousColumnSpanBlock();
if (destroyColumns) {
if (hasColumns()) {
delete gColumnInfoMap->take(this);
@@ -4209,133 +4435,75 @@ ColumnInfo* RenderBlock::columnInfo() const
return gColumnInfoMap->get(this);
}
-int RenderBlock::layoutColumns(int endOfContent, int requestedColumnHeight)
+unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const
{
- // Don't do anything if we have no columns
- if (!hasColumns())
- return -1;
+ ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
+ return colInfo->columnCount();
+}
- ColumnInfo* info = gColumnInfoMap->get(this);
- int desiredColumnWidth = info->desiredColumnWidth();
- int desiredColumnCount = info->desiredColumnCount();
-
- bool computeIntrinsicHeight = (endOfContent == -1);
-
- // Fill the columns in to the available height. Attempt to balance the height of the columns.
- // Add in half our line-height to help with best-guess initial balancing.
- int columnSlop = lineHeight(false) / 2;
- int remainingSlopSpace = columnSlop * desiredColumnCount;
- int availableHeight = contentHeight();
- int colHeight;
- if (computeIntrinsicHeight && requestedColumnHeight >= 0)
- colHeight = requestedColumnHeight;
- else if (computeIntrinsicHeight)
- colHeight = min(availableHeight, availableHeight / desiredColumnCount + columnSlop);
- else
- colHeight = availableHeight;
- int originalColHeight = colHeight;
+IntRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const
+{
+ ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
+ // Compute the appropriate rect based off our information.
+ int colWidth = colInfo->desiredColumnWidth();
+ int colHeight = colInfo->columnHeight();
+ int colTop = borderTop() + paddingTop();
int colGap = columnGap();
+ int colLeft = style()->direction() == LTR ?
+ borderLeft() + paddingLeft() + (index * (colWidth + colGap))
+ : borderLeft() + paddingLeft() + contentWidth() - colWidth - (index * (colWidth + colGap));
+ return IntRect(colLeft, colTop, colWidth, colHeight);
+}
- // Compute a collection of column rects.
- info->clearColumns();
-
- // Then we do a simulated "paint" into the column slices and allow the content to slightly adjust our individual column rects.
- // FIXME: We need to take into account layers that are affected by the columns as well here so that they can have an opportunity
- // to adjust column rects also.
- RenderView* v = view();
- int left = borderLeft() + paddingLeft();
- int top = borderTop() + paddingTop();
- int currX = style()->direction() == LTR ? borderLeft() + paddingLeft() : borderLeft() + paddingLeft() + contentWidth() - desiredColumnWidth;
- int currY = top;
- unsigned colCount = desiredColumnCount;
- int maxColBottom = borderTop() + paddingTop();
- int contentBottom = top + availableHeight;
- int minimumColumnHeight = -1;
- for (unsigned i = 0; i < colCount; i++) {
- // If we aren't constrained, then the last column can just get all the remaining space.
- if (computeIntrinsicHeight && i == colCount - 1)
- colHeight = availableHeight;
-
- // This represents the real column position.
- IntRect colRect(currX, top, desiredColumnWidth, colHeight);
-
- int truncationPoint = visibleTopOfHighestFloatExtendingBelow(currY + colHeight, colHeight);
-
- // For the simulated paint, we pretend like everything is in one long strip.
- IntRect pageRect(left, currY, contentWidth(), truncationPoint - currY);
- v->setPrintRect(pageRect);
- v->setTruncatedAt(truncationPoint);
- GraphicsContext context((PlatformGraphicsContext*)0);
- PaintInfo paintInfo(&context, pageRect, PaintPhaseForeground, false, 0, 0);
+bool RenderBlock::layoutColumns(bool hasSpecifiedPageHeight, int pageHeight, LayoutStateMaintainer& statePusher)
+{
+ if (hasColumns()) {
+ // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what
+ // the distance between forced page breaks is so that we can avoid making the minimum column height too tall.
+ ColumnInfo* colInfo = columnInfo();
+ int desiredColumnCount = colInfo->desiredColumnCount();
+ if (!hasSpecifiedPageHeight) {
+ int columnHeight = pageHeight;
+ int minColumnCount = colInfo->forcedBreaks() + 1;
+ if (minColumnCount >= desiredColumnCount) {
+ // The forced page breaks are in control of the balancing. Just set the column height to the
+ // maximum page break distance.
+ if (!pageHeight) {
+ int distanceBetweenBreaks = max(colInfo->maximumDistanceBetweenForcedBreaks(),
+ view()->layoutState()->pageY(borderTop() + paddingTop() + contentHeight()) - colInfo->forcedBreakOffset());
+ columnHeight = max(colInfo->minimumColumnHeight(), distanceBetweenBreaks);
+ }
+ } else if (contentHeight() > pageHeight * desiredColumnCount) {
+ // Now that we know the intrinsic height of the columns, we have to rebalance them.
+ columnHeight = max(colInfo->minimumColumnHeight(), (int)ceilf((float)contentHeight() / desiredColumnCount));
+ }
+
+ if (columnHeight && columnHeight != pageHeight) {
+ statePusher.pop();
+ m_everHadLayout = true;
+ layoutBlock(false, columnHeight);
+ return true;
+ }
+ }
- setHasColumns(false);
- paintObject(paintInfo, 0, 0);
- setHasColumns(true);
-
- if (computeIntrinsicHeight && v->minimumColumnHeight() > originalColHeight) {
- // The initial column height was too small to contain one line of text.
- minimumColumnHeight = max(minimumColumnHeight, v->minimumColumnHeight());
- }
+ if (pageHeight) // FIXME: Should we use lowestPosition (excluding our positioned objects) instead of contentHeight()?
+ colInfo->setColumnCountAndHeight(ceilf((float)contentHeight() / pageHeight), pageHeight);
+
+ if (columnCount(colInfo)) {
+ IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1);
+ int overflowLeft = style()->direction() == RTL ? min(0, lastRect.x()) : 0;
+ int overflowRight = style()->direction() == LTR ? max(width(), lastRect.x() + lastRect.width()) : 0;
+ int overflowHeight = borderTop() + paddingTop() + colInfo->columnHeight();
+
+ setHeight(overflowHeight + borderBottom() + paddingBottom() + horizontalScrollbarHeight());
- int adjustedBottom = v->bestTruncatedAt();
- if (adjustedBottom <= currY)
- adjustedBottom = truncationPoint;
-
- colRect.setHeight(adjustedBottom - currY);
-
- // Add in the lost space to the subsequent columns.
- // FIXME: This will create a "staircase" effect if there are enough columns, but the effect should be pretty subtle.
- if (computeIntrinsicHeight) {
- int lostSpace = colHeight - colRect.height();
- if (lostSpace > remainingSlopSpace) {
- // Redestribute the space among the remaining columns.
- int spaceToRedistribute = lostSpace - remainingSlopSpace;
- int remainingColumns = colCount - i + 1;
- colHeight += spaceToRedistribute / remainingColumns;
- }
- remainingSlopSpace = max(0, remainingSlopSpace - lostSpace);
+ m_overflow.clear();
+ addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight));
}
-
- if (style()->direction() == LTR)
- currX += desiredColumnWidth + colGap;
- else
- currX -= (desiredColumnWidth + colGap);
-
- currY += colRect.height();
- availableHeight -= colRect.height();
-
- maxColBottom = max(colRect.bottom(), maxColBottom);
-
- info->addColumnRect(colRect);
-
- // Start adding in more columns as long as there's still content left.
- if (currY < endOfContent && i == colCount - 1 && (computeIntrinsicHeight || contentHeight()))
- colCount++;
- }
-
- if (minimumColumnHeight >= 0) {
- // If originalColHeight was too small, we need to try to layout again.
- return layoutColumns(endOfContent, minimumColumnHeight);
}
-
- int overflowRight = max(width(), currX - colGap);
- int overflowLeft = min(0, currX + desiredColumnWidth + colGap);
- int overflowHeight = maxColBottom;
- int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
-
- if (computeIntrinsicHeight)
- setHeight(maxColBottom + toAdd);
-
- m_overflow.clear();
- addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight));
-
- v->setPrintRect(IntRect());
- v->setTruncatedAt(0);
- ASSERT(colCount == info->columnCount());
-
- return contentBottom;
+ return false;
}
void RenderBlock::adjustPointToColumnContents(IntPoint& point) const
@@ -4345,17 +4513,17 @@ void RenderBlock::adjustPointToColumnContents(IntPoint& point) const
return;
ColumnInfo* colInfo = columnInfo();
- if (!colInfo->columnCount())
+ if (!columnCount(colInfo))
return;
// Determine which columns we intersect.
int colGap = columnGap();
int leftGap = colGap / 2;
- IntPoint columnPoint(colInfo->columnRectAt(0).location());
+ IntPoint columnPoint(columnRectAt(colInfo, 0).location());
int yOffset = 0;
for (unsigned i = 0; i < colInfo->columnCount(); i++) {
// Add in half the column gap to the left and right of the rect.
- IntRect colRect = colInfo->columnRectAt(i);
+ IntRect colRect = columnRectAt(colInfo, i);
IntRect gapAndColumnRect(colRect.x() - leftGap, colRect.y(), colRect.width() + colGap, colRect.height());
if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.right()) {
@@ -4393,7 +4561,7 @@ void RenderBlock::adjustRectForColumns(IntRect& r) const
IntRect result;
// Determine which columns we intersect.
- unsigned colCount = colInfo->columnCount();
+ unsigned colCount = columnCount(colInfo);
if (!colCount)
return;
@@ -4401,7 +4569,7 @@ void RenderBlock::adjustRectForColumns(IntRect& r) const
int currYOffset = 0;
for (unsigned i = 0; i < colCount; i++) {
- IntRect colRect = colInfo->columnRectAt(i);
+ IntRect colRect = columnRectAt(colInfo, i);
int currXOffset = colRect.x() - left;
IntRect repaintRect = r;
@@ -4427,9 +4595,9 @@ void RenderBlock::adjustForColumns(IntSize& offset, const IntPoint& point) const
int left = borderLeft() + paddingLeft();
int yOffset = 0;
- size_t columnCount = colInfo->columnCount();
- for (size_t i = 0; i < columnCount; ++i) {
- IntRect columnRect = colInfo->columnRectAt(i);
+ size_t colCount = columnCount(colInfo);
+ for (size_t i = 0; i < colCount; ++i) {
+ IntRect columnRect = columnRectAt(colInfo, i);
int xOffset = columnRect.x() - left;
if (point.y() < columnRect.bottom() + yOffset) {
offset.expand(xOffset, -yOffset);
@@ -5108,6 +5276,16 @@ RenderBlock* RenderBlock::firstLineBlock() const
return firstLineBlock;
}
+static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer)
+{
+ RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle());
+ // Force inline display (except for floating first-letters).
+ pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
+ // CSS2 says first-letter can't be positioned.
+ pseudoStyle->setPosition(StaticPosition);
+ return pseudoStyle;
+}
+
void RenderBlock::updateFirstLetter()
{
if (!document()->usesFirstLetterRules())
@@ -5157,84 +5335,110 @@ void RenderBlock::updateFirstLetter()
if (!currChild)
return;
- RenderObject* firstLetterContainer = currChild->parent();
-
// If the child already has style, then it has already been created, so we just want
// to update it.
- if (firstLetterContainer->style()->styleType() == FIRST_LETTER) {
- RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER,
- firstLetterContainer->parent()->firstLineStyle());
- firstLetterContainer->setStyle(pseudo);
- for (RenderObject* genChild = firstLetterContainer->firstChild(); genChild; genChild = genChild->nextSibling()) {
+ if (currChild->parent()->style()->styleType() == FIRST_LETTER) {
+ RenderObject* firstLetter = currChild->parent();
+ RenderObject* firstLetterContainer = firstLetter->parent();
+ RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
+
+ if (Node::diff(firstLetter->style(), pseudoStyle) == Node::Detach) {
+ // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
+ RenderObject* newFirstLetter;
+ if (pseudoStyle->display() == INLINE)
+ newFirstLetter = new (renderArena()) RenderInline(document());
+ else
+ newFirstLetter = new (renderArena()) RenderBlock(document());
+ newFirstLetter->setStyle(pseudoStyle);
+
+ // Move the first letter into the new renderer.
+ view()->disableLayoutState();
+ while (RenderObject* child = firstLetter->firstChild()) {
+ if (child->isText())
+ toRenderText(child)->dirtyLineBoxes(true);
+ firstLetter->removeChild(child);
+ newFirstLetter->addChild(child, 0);
+ }
+ RenderTextFragment* remainingText = toRenderTextFragment(firstLetter->nextSibling());
+ ASSERT(remainingText->node()->renderer() == remainingText);
+ // Replace the old renderer with the new one.
+ remainingText->setFirstLetter(newFirstLetter);
+ firstLetter->destroy();
+ firstLetter = newFirstLetter;
+ firstLetterContainer->addChild(firstLetter, remainingText);
+ view()->enableLayoutState();
+ } else
+ firstLetter->setStyle(pseudoStyle);
+
+ for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
if (genChild->isText())
- genChild->setStyle(pseudo);
+ genChild->setStyle(pseudoStyle);
}
+
return;
}
+ if (!currChild->isText() || currChild->isBR())
+ return;
+
// If the child does not already have style, we create it here.
- if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != FIRST_LETTER) {
- // Our layout state is not valid for the repaints we are going to trigger by
- // adding and removing children of firstLetterContainer.
- view()->disableLayoutState();
+ RenderObject* firstLetterContainer = currChild->parent();
- RenderText* textObj = toRenderText(currChild);
-
- // Create our pseudo style now that we have our firstLetterContainer determined.
- RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER,
- firstLetterContainer->firstLineStyle());
-
- // Force inline display (except for floating first-letters)
- pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
- pseudoStyle->setPosition(StaticPosition); // CSS2 says first-letter can't be positioned.
-
- RenderObject* firstLetter = 0;
- if (pseudoStyle->display() == INLINE)
- firstLetter = new (renderArena()) RenderInline(document());
- else
- firstLetter = new (renderArena()) RenderBlock(document());
- firstLetter->setStyle(pseudoStyle);
- firstLetterContainer->addChild(firstLetter, currChild);
-
- // The original string is going to be either a generated content string or a DOM node's
- // string. We want the original string before it got transformed in case first-letter has
- // no text-transform or a different text-transform applied to it.
- RefPtr<StringImpl> oldText = textObj->originalText();
- ASSERT(oldText);
-
- if (oldText && oldText->length() > 0) {
- unsigned int length = 0;
-
- // account for leading spaces and punctuation
- while (length < oldText->length() && (isSpaceOrNewline((*oldText)[length]) || Unicode::isPunct((*oldText)[length])))
- length++;
-
- // account for first letter
+ // Our layout state is not valid for the repaints we are going to trigger by
+ // adding and removing children of firstLetterContainer.
+ view()->disableLayoutState();
+
+ RenderText* textObj = toRenderText(currChild);
+
+ // Create our pseudo style now that we have our firstLetterContainer determined.
+ RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
+
+ RenderObject* firstLetter = 0;
+ if (pseudoStyle->display() == INLINE)
+ firstLetter = new (renderArena()) RenderInline(document());
+ else
+ firstLetter = new (renderArena()) RenderBlock(document());
+ firstLetter->setStyle(pseudoStyle);
+ firstLetterContainer->addChild(firstLetter, currChild);
+
+ // The original string is going to be either a generated content string or a DOM node's
+ // string. We want the original string before it got transformed in case first-letter has
+ // no text-transform or a different text-transform applied to it.
+ RefPtr<StringImpl> oldText = textObj->originalText();
+ ASSERT(oldText);
+
+ if (oldText && oldText->length() > 0) {
+ unsigned length = 0;
+
+ // account for leading spaces and punctuation
+ while (length < oldText->length() && (isSpaceOrNewline((*oldText)[length]) || Unicode::isPunct((*oldText)[length])))
length++;
-
- // construct text fragment for the text after the first letter
- // NOTE: this might empty
- RenderTextFragment* remainingText =
- new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length);
- remainingText->setStyle(textObj->style());
- if (remainingText->node())
- remainingText->node()->setRenderer(remainingText);
-
- RenderObject* nextObj = textObj->nextSibling();
- firstLetterContainer->removeChild(textObj);
- firstLetterContainer->addChild(remainingText, nextObj);
- remainingText->setFirstLetter(firstLetter);
-
- // construct text fragment for the first letter
- RenderTextFragment* letter =
- new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length);
- letter->setStyle(pseudoStyle);
- firstLetter->addChild(letter);
- textObj->destroy();
- }
- view()->enableLayoutState();
+ // account for first letter
+ length++;
+
+ // construct text fragment for the text after the first letter
+ // NOTE: this might empty
+ RenderTextFragment* remainingText =
+ new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length);
+ remainingText->setStyle(textObj->style());
+ if (remainingText->node())
+ remainingText->node()->setRenderer(remainingText);
+
+ RenderObject* nextObj = textObj->nextSibling();
+ firstLetterContainer->removeChild(textObj);
+ firstLetterContainer->addChild(remainingText, nextObj);
+ remainingText->setFirstLetter(firstLetter);
+
+ // construct text fragment for the first letter
+ RenderTextFragment* letter =
+ new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length);
+ letter->setStyle(pseudoStyle);
+ firstLetter->addChild(letter);
+
+ textObj->destroy();
}
+ view()->enableLayoutState();
}
// Helper methods for obtaining the last line, computing line counts and heights for line counts
@@ -5407,24 +5611,44 @@ void RenderBlock::clearTruncation()
void RenderBlock::setMaxTopMargins(int pos, int neg)
{
- if (!m_maxMargin) {
- if (pos == MaxMargin::topPosDefault(this) && neg == MaxMargin::topNegDefault(this))
+ if (!m_rareData) {
+ if (pos == RenderBlockRareData::topPosDefault(this) && neg == RenderBlockRareData::topNegDefault(this))
return;
- m_maxMargin = new MaxMargin(this);
+ m_rareData = new RenderBlockRareData(this);
}
- m_maxMargin->m_topPos = pos;
- m_maxMargin->m_topNeg = neg;
+ m_rareData->m_topPos = pos;
+ m_rareData->m_topNeg = neg;
}
void RenderBlock::setMaxBottomMargins(int pos, int neg)
{
- if (!m_maxMargin) {
- if (pos == MaxMargin::bottomPosDefault(this) && neg == MaxMargin::bottomNegDefault(this))
+ if (!m_rareData) {
+ if (pos == RenderBlockRareData::bottomPosDefault(this) && neg == RenderBlockRareData::bottomNegDefault(this))
+ return;
+ m_rareData = new RenderBlockRareData(this);
+ }
+ m_rareData->m_bottomPos = pos;
+ m_rareData->m_bottomNeg = neg;
+}
+
+void RenderBlock::setPaginationStrut(int strut)
+{
+ if (!m_rareData) {
+ if (!strut)
return;
- m_maxMargin = new MaxMargin(this);
+ m_rareData = new RenderBlockRareData(this);
}
- m_maxMargin->m_bottomPos = pos;
- m_maxMargin->m_bottomNeg = neg;
+ m_rareData->m_paginationStrut = strut;
+}
+
+void RenderBlock::setPageY(int y)
+{
+ if (!m_rareData) {
+ if (!y)
+ return;
+ m_rareData = new RenderBlockRareData(this);
+ }
+ m_rareData->m_pageY = y;
}
void RenderBlock::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
@@ -5685,6 +5909,119 @@ RenderBlock* RenderBlock::createAnonymousColumnSpanBlock() const
return newBox;
}
+int RenderBlock::nextPageTop(int yPos) const
+{
+ LayoutState* layoutState = view()->layoutState();
+ if (!layoutState->m_pageHeight)
+ return yPos;
+
+ // The yPos is in our coordinate space. We can add in our pushed offset.
+ int pageHeight = layoutState->m_pageHeight;
+ int remainingHeight = (pageHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageHeight) % pageHeight;
+ return yPos + remainingHeight;
+}
+
+static bool inNormalFlow(RenderBox* child)
+{
+ RenderBlock* curr = child->containingBlock();
+ RenderBlock* initialBlock = child->view();
+ while (curr && curr != initialBlock) {
+ if (curr->hasColumns())
+ return true;
+ if (curr->isFloatingOrPositioned())
+ return false;
+ curr = curr->containingBlock();
+ }
+ return true;
+}
+
+int RenderBlock::applyBeforeBreak(RenderBox* child, int yPos)
+{
+ // FIXME: Add page break checking here when we support printing.
+ bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns();
+ bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageHeight; // FIXME: Once columns can print we have to check this.
+ bool checkBeforeAlways = (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS);
+ if (checkBeforeAlways && inNormalFlow(child)) {
+ if (checkColumnBreaks)
+ view()->layoutState()->addForcedColumnBreak(yPos);
+ return nextPageTop(yPos);
+ }
+ return yPos;
+}
+
+int RenderBlock::applyAfterBreak(RenderBox* child, int yPos, MarginInfo& marginInfo)
+{
+ // FIXME: Add page break checking here when we support printing.
+ bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns();
+ bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageHeight; // FIXME: Once columns can print we have to check this.
+ bool checkAfterAlways = (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS);
+ if (checkAfterAlways && inNormalFlow(child)) {
+ marginInfo.setBottomQuirk(true); // Cause margins to be discarded for any following content.
+ if (checkColumnBreaks)
+ view()->layoutState()->addForcedColumnBreak(yPos);
+ return nextPageTop(yPos);
+ }
+ return yPos;
+}
+
+int RenderBlock::adjustForUnsplittableChild(RenderBox* child, int yPos, bool includeMargins)
+{
+ bool isUnsplittable = child->isReplaced() || child->scrollsOverflow();
+ if (!isUnsplittable)
+ return yPos;
+ int childHeight = child->height() + (includeMargins ? child->marginTop() + child->marginBottom() : 0);
+ LayoutState* layoutState = view()->layoutState();
+ if (layoutState->m_columnInfo)
+ layoutState->m_columnInfo->updateMinimumColumnHeight(childHeight);
+ int pageHeight = layoutState->m_pageHeight;
+ if (!pageHeight || childHeight > pageHeight)
+ return yPos;
+ int remainingHeight = (pageHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageHeight) % pageHeight;
+ if (remainingHeight < childHeight)
+ return yPos + remainingHeight;
+ return yPos;
+}
+
+void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, int& delta)
+{
+ // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we
+ // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since
+ // the line on the top of the next page will appear too far down relative to the same kind of line at the top
+ // of the first column.
+ //
+ // The rendering we would like to see is one where the lineTop is at the top of the column, and any line overflow
+ // simply spills out above the top of the column. This effect would match what happens at the top of the first column.
+ // We can't achieve this rendering, however, until we stop columns from clipping to the column bounds (thus allowing
+ // for overflow to occur), and then cache visible overflow for each column rect.
+ //
+ // Furthermore, the paint we have to do when a column has overflow has to be special. We need to exclude
+ // content that paints in a previous column (and content that paints in the following column).
+ //
+ // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats).
+ // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the
+ // line and all following lines.
+ LayoutState* layoutState = view()->layoutState();
+ int pageHeight = layoutState->m_pageHeight;
+ int yPos = lineBox->topVisibleOverflow();
+ int lineHeight = lineBox->bottomVisibleOverflow() - yPos;
+ if (layoutState->m_columnInfo)
+ layoutState->m_columnInfo->updateMinimumColumnHeight(lineHeight);
+ yPos += delta;
+ lineBox->setPaginationStrut(0);
+ if (!pageHeight || lineHeight > pageHeight)
+ return;
+ int remainingHeight = pageHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageHeight;
+ if (remainingHeight < lineHeight) {
+ int totalHeight = lineHeight + max(0, yPos);
+ if (lineBox == firstRootBox() && totalHeight < pageHeight && !isPositioned() && !isTableCell())
+ setPaginationStrut(remainingHeight + max(0, yPos));
+ else {
+ delta += remainingHeight;
+ lineBox->setPaginationStrut(remainingHeight);
+ }
+ }
+}
+
const char* RenderBlock::renderName() const
{
if (isBody())