This source file includes following definitions.
- m_minimumColumnHeight
- createAnonymous
- heightAdjustedForSetOffset
- pageLogicalTopForOffset
- setAndConstrainColumnHeight
- findRunWithTallestColumns
- distributeImplicitBreaks
- calculateBalancedHeight
- clearForcedBreaks
- addForcedBreak
- recalculateBalancedHeight
- recordSpaceShortage
- updateLogicalWidth
- prepareForLayout
- computeLogicalHeight
- columnGap
- columnCount
- columnRectAt
- columnIndexAtOffset
- flowThreadPortionRectAt
- flowThreadPortionOverflowRect
- paintObject
- paintColumnRules
- repaintFlowThreadContent
- collectLayerFragments
- renderName
#include "config.h"
#include "core/rendering/RenderMultiColumnSet.h"
#include "core/rendering/PaintInfo.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderMultiColumnFlowThread.h"
using namespace std;
namespace WebCore {
RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
: RenderRegionSet(0, flowThread)
, m_computedColumnCount(1)
, m_computedColumnWidth(0)
, m_computedColumnHeight(0)
, m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
, m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
, m_minimumColumnHeight(0)
{
}
RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread)
{
Document& document = flowThread->document();
RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
renderer->setDocumentForAnonymous(&document);
return renderer;
}
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const
{
RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore() - multicolBlock->paddingBefore();
height -= contentLogicalTop;
return max(height, LayoutUnit(1));
}
LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
{
LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x());
unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
return portionLogicalTop + columnIndex * computedColumnHeight();
}
void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
{
m_computedColumnHeight = newHeight;
if (m_computedColumnHeight > m_maxColumnHeight)
m_computedColumnHeight = m_maxColumnHeight;
}
unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
{
unsigned indexWithLargestHeight = 0;
LayoutUnit largestHeight;
LayoutUnit previousOffset;
size_t runCount = m_contentRuns.size();
ASSERT(runCount);
for (size_t i = 0; i < runCount; i++) {
const ContentRun& run = m_contentRuns[i];
LayoutUnit height = run.columnLogicalHeight(previousOffset);
if (largestHeight < height) {
largestHeight = height;
indexWithLargestHeight = i;
}
previousOffset = run.breakOffset();
}
return indexWithLargestHeight;
}
void RenderMultiColumnSet::distributeImplicitBreaks()
{
unsigned breakCount = forcedBreaksCount();
#ifndef NDEBUG
for (unsigned i = 0; i < breakCount; i++)
ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
#endif
ASSERT(breakCount >= 1);
while (breakCount < m_computedColumnCount) {
unsigned index = findRunWithTallestColumns();
m_contentRuns[index].assumeAnotherImplicitBreak();
breakCount++;
}
}
LayoutUnit RenderMultiColumnSet::calculateBalancedHeight(bool initial) const
{
if (initial) {
unsigned index = findRunWithTallestColumns();
LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : LayoutUnit();
return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
}
if (columnCount() <= computedColumnCount()) {
return m_computedColumnHeight;
}
if (forcedBreaksCount() >= computedColumnCount()) {
return m_computedColumnHeight;
}
ASSERT(m_minSpaceShortage > 0);
ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight());
if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight())
return m_computedColumnHeight;
return m_computedColumnHeight + m_minSpaceShortage;
}
void RenderMultiColumnSet::clearForcedBreaks()
{
m_contentRuns.clear();
}
void RenderMultiColumnSet::addForcedBreak(LayoutUnit offsetFromFirstPage)
{
if (!multiColumnFlowThread()->requiresBalancing())
return;
if (!m_contentRuns.isEmpty() && offsetFromFirstPage <= m_contentRuns.last().breakOffset())
return;
if (m_contentRuns.size() < m_computedColumnCount)
m_contentRuns.append(ContentRun(offsetFromFirstPage));
}
bool RenderMultiColumnSet::recalculateBalancedHeight(bool initial)
{
ASSERT(multiColumnFlowThread()->requiresBalancing());
LayoutUnit oldColumnHeight = m_computedColumnHeight;
if (initial)
distributeImplicitBreaks();
LayoutUnit newColumnHeight = calculateBalancedHeight(initial);
setAndConstrainColumnHeight(newColumnHeight);
if (m_computedColumnHeight == oldColumnHeight)
return false;
m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
clearForcedBreaks();
return true;
}
void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
{
if (spaceShortage >= m_minSpaceShortage)
return;
ASSERT(spaceShortage > 0);
m_minSpaceShortage = spaceShortage;
}
void RenderMultiColumnSet::updateLogicalWidth()
{
RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
setComputedColumnWidthAndCount(flowThread->columnWidth(), flowThread->columnCount());
setLogicalWidth(parentBox()->contentLogicalWidth());
unsigned colCount = columnCount();
LayoutUnit colGap = columnGap();
LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap;
LayoutUnit currentContentLogicalWidth = contentLogicalWidth();
LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth);
if (!delta)
return;
setLogicalWidth(logicalWidth() + delta);
}
void RenderMultiColumnSet::prepareForLayout()
{
RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
RenderStyle* multicolStyle = multicolBlock->style();
ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet());
setLogicalTop(multicolBlock->borderBefore() + multicolBlock->paddingBefore());
updateLogicalWidth();
if (multicolBlock->multiColumnFlowThread()->requiresBalancing()) {
m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
if (!multicolStyle->logicalHeight().isAuto()) {
m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight(), -1);
if (m_maxColumnHeight == -1)
m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
}
if (!multicolStyle->logicalMaxHeight().isUndefined()) {
LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1);
if (logicalMaxHeight != -1 && m_maxColumnHeight > logicalMaxHeight)
m_maxColumnHeight = logicalMaxHeight;
}
m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight);
m_computedColumnHeight = 0;
} else {
setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->multiColumnFlowThread()->columnHeightAvailable()));
}
clearForcedBreaks();
m_minimumColumnHeight = 0;
}
void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
{
computedValues.m_extent = m_computedColumnHeight;
computedValues.m_position = logicalTop;
}
LayoutUnit RenderMultiColumnSet::columnGap() const
{
RenderBlockFlow* parentBlock = multiColumnBlockFlow();
if (parentBlock->style()->hasNormalColumnGap())
return parentBlock->style()->fontDescription().computedPixelSize();
return parentBlock->style()->columnGap();
}
unsigned RenderMultiColumnSet::columnCount() const
{
if (!computedColumnHeight())
return 1;
LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
if (!logicalHeightInColumns)
return 1;
unsigned count = ceil(logicalHeightInColumns.toFloat() / computedColumnHeight().toFloat());
ASSERT(count >= 1);
return count;
}
LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
{
LayoutUnit colLogicalWidth = computedColumnWidth();
LayoutUnit colLogicalHeight = computedColumnHeight();
LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
LayoutUnit colGap = columnGap();
if (style()->isLeftToRightDirection())
colLogicalLeft += index * (colLogicalWidth + colGap);
else
colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
if (isHorizontalWritingMode())
return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
}
unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const
{
LayoutRect portionRect(flowThreadPortionRect());
LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x();
if (offset < flowThreadLogicalTop)
return 0;
if (mode == ClampToExistingColumns) {
LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX();
if (offset >= flowThreadLogicalBottom)
return columnCount() - 1;
}
return (offset - flowThreadLogicalTop).toFloat() / computedColumnHeight().toFloat();
}
LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
{
LayoutRect portionRect = flowThreadPortionRect();
if (isHorizontalWritingMode())
portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * computedColumnHeight(), portionRect.width(), computedColumnHeight());
else
portionRect = LayoutRect(portionRect.x() + index * computedColumnHeight(), portionRect.y(), computedColumnHeight(), portionRect.height());
return portionRect;
}
LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const
{
bool isFirstColumn = !index;
bool isLastColumn = index == colCount - 1;
bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn;
bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn;
LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion());
if (isHorizontalWritingMode()) {
if (!isLeftmostColumn)
overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
if (!isRightmostColumn)
overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2);
} else {
if (!isLeftmostColumn)
overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
if (!isRightmostColumn)
overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2);
}
return overflowRect;
}
void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (style()->visibility() != VISIBLE)
return;
RenderBlockFlow::paintObject(paintInfo, paintOffset);
if (!m_flowThread || !isValid() || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
return;
paintColumnRules(paintInfo, paintOffset);
}
void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (paintInfo.context->paintingDisabled())
return;
RenderStyle* blockStyle = multiColumnBlockFlow()->style();
const Color& ruleColor = resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor);
bool ruleTransparent = blockStyle->columnRuleIsTransparent();
EBorderStyle ruleStyle = blockStyle->columnRuleStyle();
LayoutUnit ruleThickness = blockStyle->columnRuleWidth();
LayoutUnit colGap = columnGap();
bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
if (!renderRule)
return;
unsigned colCount = columnCount();
if (colCount <= 1)
return;
bool antialias = shouldAntialiasLines(paintInfo.context);
bool leftToRight = style()->isLeftToRightDirection();
LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
LayoutUnit ruleAdd = borderAndPaddingLogicalLeft();
LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
LayoutUnit inlineDirectionSize = computedColumnWidth();
BoxSide boxSide = isHorizontalWritingMode()
? leftToRight ? BSLeft : BSRight
: leftToRight ? BSTop : BSBottom;
for (unsigned i = 0; i < colCount; i++) {
if (leftToRight) {
ruleLogicalLeft += inlineDirectionSize + colGap / 2;
currLogicalLeftOffset += inlineDirectionSize + colGap;
} else {
ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
currLogicalLeftOffset -= (inlineDirectionSize + colGap);
}
if (i < colCount - 1) {
LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
}
ruleLogicalLeft = currLogicalLeftOffset;
}
}
void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect) const
{
LayoutRect flowThreadRepaintRect(repaintRect);
flowThread()->flipForWritingMode(flowThreadRepaintRect);
LayoutRect clippedRect(flowThreadRepaintRect);
clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
if (clippedRect.isEmpty())
return;
LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x();
LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1;
unsigned startColumn = columnIndexAtOffset(repaintLogicalTop);
unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom);
LayoutUnit colGap = columnGap();
unsigned colCount = columnCount();
for (unsigned i = startColumn; i <= endColumn; i++) {
LayoutRect colRect = columnRectAt(i);
LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
repaintFlowThreadContentRectangle(repaintRect, flowThreadPortion, flowThreadOverflowPortion, colRect.location());
}
}
void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
{
LayoutRect layerBoundsInFlowThread(layerBoundingBox);
flowThread()->flipForWritingMode(layerBoundsInFlowThread);
LayoutRect clippedRect(layerBoundsInFlowThread);
clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
if (clippedRect.isEmpty())
return;
LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
LayoutUnit colLogicalWidth = computedColumnWidth();
LayoutUnit colGap = columnGap();
unsigned colCount = columnCount();
for (unsigned i = startColumn; i <= endColumn; i++) {
LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
LayoutRect clippedRect(layerBoundsInFlowThread);
clippedRect.intersect(flowThreadOverflowPortion);
if (clippedRect.isEmpty())
continue;
LayoutPoint translationOffset;
LayoutUnit inlineOffset = i * (colLogicalWidth + colGap);
if (!style()->isLeftToRightDirection())
inlineOffset = -inlineOffset;
translationOffset.setX(inlineOffset);
LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x();
if (isFlippedBlocksWritingMode(style()->writingMode()))
blockOffset = -blockOffset;
translationOffset.setY(blockOffset);
if (!isHorizontalWritingMode())
translationOffset = translationOffset.transposedPoint();
LayoutRect translatedDirtyRect(dirtyRect);
translatedDirtyRect.moveBy(-translationOffset);
clippedRect = layerBoundingBox;
clippedRect.intersect(translatedDirtyRect);
if (clippedRect.isEmpty())
continue;
LayerFragment fragment;
fragment.paginationOffset = translationOffset;
LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
flowThread()->flipForWritingMode(flippedFlowThreadOverflowPortion);
fragment.paginationClip = flippedFlowThreadOverflowPortion;
fragments.append(fragment);
}
}
const char* RenderMultiColumnSet::renderName() const
{
return "RenderMultiColumnSet";
}
}