RenderMultiColumnSet.cpp [plain text]
#include "config.h"
#include "RenderMultiColumnSet.h"
#include "FrameView.h"
#include "HitTestResult.h"
#include "PaintInfo.h"
#include "RenderLayer.h"
#include "RenderMultiColumnFlowThread.h"
#include "RenderMultiColumnSpannerPlaceholder.h"
#include "RenderView.h"
namespace WebCore {
RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread& flowThread, PassRef<RenderStyle> style)
: RenderRegionSet(flowThread.document(), WTF::move(style), flowThread)
, m_computedColumnCount(1)
, m_computedColumnWidth(0)
, m_computedColumnHeight(0)
, m_availableColumnHeight(0)
, m_columnHeightComputed(false)
, m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
, m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
, m_minimumColumnHeight(0)
{
}
RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const
{
for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) {
if (sibling->isRenderMultiColumnSet())
return toRenderMultiColumnSet(sibling);
}
return nullptr;
}
RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() const
{
for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) {
if (sibling->isRenderMultiColumnSet())
return toRenderMultiColumnSet(sibling);
}
return nullptr;
}
RenderObject* RenderMultiColumnSet::firstRendererInFlowThread() const
{
if (RenderBox* sibling = RenderMultiColumnFlowThread::previousColumnSetOrSpannerSiblingOf(this)) {
ASSERT(!sibling->isRenderMultiColumnSet());
RenderMultiColumnSpannerPlaceholder* placeholder = multiColumnFlowThread()->findColumnSpannerPlaceholder(sibling);
return placeholder->nextInPreOrderAfterChildren();
}
return flowThread()->firstChild();
}
RenderObject* RenderMultiColumnSet::lastRendererInFlowThread() const
{
if (RenderBox* sibling = RenderMultiColumnFlowThread::nextColumnSetOrSpannerSiblingOf(this)) {
ASSERT(!sibling->isRenderMultiColumnSet());
RenderMultiColumnSpannerPlaceholder* placeholder = multiColumnFlowThread()->findColumnSpannerPlaceholder(sibling);
return placeholder->previousInPreOrder();
}
return flowThread()->lastLeafChild();
}
static bool precedesRenderer(RenderObject* renderer, RenderObject* boundary)
{
for (; renderer; renderer = renderer->nextInPreOrder()) {
if (renderer == boundary)
return true;
}
return false;
}
bool RenderMultiColumnSet::containsRendererInFlowThread(RenderObject* renderer) const
{
if (!previousSiblingMultiColumnSet() && !nextSiblingMultiColumnSet()) {
return renderer->isDescendantOf(m_flowThread);
}
RenderObject* firstRenderer = firstRendererInFlowThread();
RenderObject* lastRenderer = lastRendererInFlowThread();
ASSERT(firstRenderer);
ASSERT(lastRenderer);
return precedesRenderer(firstRenderer, renderer) && precedesRenderer(renderer, lastRenderer);
}
void RenderMultiColumnSet::setLogicalTopInFlowThread(LayoutUnit logicalTop)
{
LayoutRect rect = flowThreadPortionRect();
if (isHorizontalWritingMode())
rect.setY(logicalTop);
else
rect.setX(logicalTop);
setFlowThreadPortionRect(rect);
}
void RenderMultiColumnSet::setLogicalBottomInFlowThread(LayoutUnit logicalBottom)
{
LayoutRect rect = flowThreadPortionRect();
if (isHorizontalWritingMode())
rect.shiftMaxYEdgeTo(logicalBottom);
else
rect.shiftMaxXEdgeTo(logicalBottom);
setFlowThreadPortionRect(rect);
}
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const
{
RenderBlockFlow* multicolBlock = toRenderBlockFlow(parent());
LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore();
height -= contentLogicalTop;
return std::max(height, LayoutUnit::fromPixel(1)); }
LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
{
unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
return logicalTopInFlowThread() + columnIndex * computedColumnHeight();
}
void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
{
m_computedColumnHeight = newHeight;
if (m_computedColumnHeight > m_maxColumnHeight)
m_computedColumnHeight = m_maxColumnHeight;
m_availableColumnHeight = m_computedColumnHeight;
if (multiColumnFlowThread() && !multiColumnFlowThread()->progressionIsInline() && parent()->isRenderView()) {
int pageLength = view().frameView().pagination().pageLength;
if (pageLength)
m_computedColumnHeight = pageLength;
}
m_columnHeightComputed = true;
}
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()
{
#ifndef NDEBUG
for (unsigned i = 0; i < forcedBreaksCount(); i++)
ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
#endif // NDEBUG
addForcedBreak(logicalBottomInFlowThread());
unsigned breakCount = forcedBreaksCount();
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() : logicalTopInFlowThread();
return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
}
if (columnCount() <= computedColumnCount()) {
return m_computedColumnHeight;
}
if (forcedBreaksCount() > 1 && forcedBreaksCount() >= computedColumnCount()) {
return m_computedColumnHeight;
}
ASSERT(m_minSpaceShortage > 0); 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 (!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::recalculateColumnHeight(bool initial)
{
LayoutUnit oldColumnHeight = m_computedColumnHeight;
if (requiresBalancing()) {
if (initial)
distributeImplicitBreaks();
LayoutUnit newColumnHeight = calculateBalancedHeight(initial);
setAndConstrainColumnHeight(newColumnHeight);
} else {
setAndConstrainColumnHeight(m_computedColumnHeight);
}
if (m_computedColumnHeight == oldColumnHeight)
return false;
m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
return true; }
void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
{
if (spaceShortage >= m_minSpaceShortage)
return;
if (spaceShortage > 0)
m_minSpaceShortage = spaceShortage;
}
void RenderMultiColumnSet::updateLogicalWidth()
{
setComputedColumnWidthAndCount(multiColumnFlowThread()->columnWidth(), multiColumnFlowThread()->columnCount());
setLogicalWidth(parentBox()->contentLogicalWidth());
}
bool RenderMultiColumnSet::requiresBalancing() const
{
if (!multiColumnFlowThread()->progressionIsInline())
return false;
if (RenderBox* next = RenderMultiColumnFlowThread::nextColumnSetOrSpannerSiblingOf(this)) {
if (!next->isRenderMultiColumnSet()) {
ASSERT(multiColumnFlowThread()->findColumnSpannerPlaceholder(next));
return true;
}
}
RenderBlockFlow* container = multiColumnBlockFlow();
if (container->style().columnFill() == ColumnFillBalance)
return true;
return !multiColumnFlowThread()->columnHeightAvailable();
}
void RenderMultiColumnSet::prepareForLayout(bool initial)
{
if (RenderBox* previous = RenderMultiColumnFlowThread::previousColumnSetOrSpannerSiblingOf(this))
setLogicalTop(previous->logicalBottom() + previous->marginAfter());
else
setLogicalTop(multiColumnBlockFlow()->borderAndPaddingBefore());
if (initial)
m_maxColumnHeight = calculateMaxColumnHeight();
if (requiresBalancing()) {
if (initial) {
m_computedColumnHeight = 0;
m_availableColumnHeight = 0;
m_columnHeightComputed = false;
}
} else
setAndConstrainColumnHeight(heightAdjustedForSetOffset(multiColumnFlowThread()->columnHeightAvailable()));
updateLogicalWidth();
clearForcedBreaks();
m_minimumColumnHeight = 0;
setLogicalBottomInFlowThread(RenderFlowThread::maxLogicalHeight());
setNeedsLayout();
}
void RenderMultiColumnSet::beginFlow(RenderBlock* container)
{
RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
LayoutUnit logicalTopInFlowThread = flowThread->offsetFromLogicalTopOfFirstRegion(container) + container->logicalHeight();
setLogicalTopInFlowThread(logicalTopInFlowThread);
}
void RenderMultiColumnSet::endFlow(RenderBlock* container, LayoutUnit bottomInContainer)
{
RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
LayoutUnit logicalBottomInFlowThread = flowThread->offsetFromLogicalTopOfFirstRegion(container) + bottomInContainer;
setLogicalBottomInFlowThread(logicalBottomInFlowThread);
container->setLogicalHeight(bottomInContainer);
}
void RenderMultiColumnSet::layout()
{
RenderBlockFlow::layout();
m_maxColumnHeight = calculateMaxColumnHeight();
if (!nextSiblingMultiColumnSet()) {
multiColumnFlowThread()->validateRegions();
}
}
void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
{
computedValues.m_extent = m_availableColumnHeight;
computedValues.m_position = logicalTop;
}
LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const
{
RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
const RenderStyle& multicolStyle = multicolBlock->style();
LayoutUnit availableHeight = multiColumnFlowThread()->columnHeightAvailable();
LayoutUnit maxColumnHeight = availableHeight ? availableHeight : RenderFlowThread::maxLogicalHeight();
if (!multicolStyle.logicalMaxHeight().isUndefined()) {
LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalMaxHeight());
if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight)
maxColumnHeight = logicalMaxHeight;
}
return heightAdjustedForSetOffset(maxColumnHeight);
}
LayoutUnit RenderMultiColumnSet::columnGap() const
{
RenderBlockFlow* parentBlock = toRenderBlockFlow(parent());
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(static_cast<float>(logicalHeightInColumns) / computedColumnHeight());
ASSERT(count >= 1);
return count;
}
LayoutUnit RenderMultiColumnSet::columnLogicalLeft(unsigned index) const
{
LayoutUnit colLogicalWidth = computedColumnWidth();
LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
LayoutUnit colGap = columnGap();
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool progressionInline = multiColumnFlowThread()->progressionIsInline();
if (progressionInline) {
if (style().isLeftToRightDirection() ^ progressionReversed)
colLogicalLeft += index * (colLogicalWidth + colGap);
else
colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
}
return colLogicalLeft;
}
LayoutUnit RenderMultiColumnSet::columnLogicalTop(unsigned index) const
{
LayoutUnit colLogicalHeight = computedColumnHeight();
LayoutUnit colLogicalTop = borderAndPaddingBefore();
LayoutUnit colGap = columnGap();
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool progressionInline = multiColumnFlowThread()->progressionIsInline();
if (!progressionInline) {
if (!progressionReversed)
colLogicalTop += index * (colLogicalHeight + colGap);
else
colLogicalTop += contentLogicalHeight() - colLogicalHeight - index * (colLogicalHeight + colGap);
}
return colLogicalTop;
}
LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
{
LayoutUnit colLogicalWidth = computedColumnWidth();
LayoutUnit colLogicalHeight = computedColumnHeight();
if (isHorizontalWritingMode())
return LayoutRect(columnLogicalLeft(index), columnLogicalTop(index), colLogicalWidth, colLogicalHeight);
return LayoutRect(columnLogicalTop(index), columnLogicalLeft(index), 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;
}
if (!computedColumnHeight())
return 0;
return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight();
}
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)
{
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool isFirstColumn = !index;
bool isLastColumn = index == colCount - 1;
bool isLeftmostColumn = style().isLeftToRightDirection() ^ progressionReversed ? isFirstColumn : isLastColumn;
bool isRightmostColumn = style().isLeftToRightDirection() ^ progressionReversed ? isLastColumn : isFirstColumn;
LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion(), VisualOverflow);
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::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (paintInfo.context->paintingDisabled())
return;
RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
const RenderStyle& blockStyle = parent()->style();
const Color& ruleColor = blockStyle.visitedDependentColor(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);
if (flowThread->progressionIsInline()) {
bool leftToRight = style().isLeftToRightDirection() ^ flowThread->progressionIsReversed();
LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
LayoutUnit ruleAdd = logicalLeftOffsetForContent();
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;
}
} else {
bool topToBottom = !style().isFlippedBlocksWritingMode() ^ flowThread->progressionIsReversed();
LayoutUnit ruleLeft = isHorizontalWritingMode() ? LayoutUnit() : colGap / 2 - colGap - ruleThickness / 2;
LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness;
LayoutUnit ruleTop = isHorizontalWritingMode() ? colGap / 2 - colGap - ruleThickness / 2 : LayoutUnit();
LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight();
LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight);
if (!topToBottom) {
if (isHorizontalWritingMode())
ruleRect.setY(height() - ruleRect.maxY());
else
ruleRect.setX(width() - ruleRect.maxX());
}
ruleRect.moveBy(paintOffset);
BoxSide boxSide = isHorizontalWritingMode() ? topToBottom ? BSTop : BSBottom : topToBottom ? BSLeft : BSRight;
LayoutSize step(0, topToBottom ? computedColumnHeight() + colGap : -(computedColumnHeight() + colGap));
if (!isHorizontalWritingMode())
step = step.transposedSize();
for (unsigned i = 1; i < colCount; i++) {
ruleRect.move(step);
IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect);
drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
}
}
}
void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect)
{
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);
flipForWritingMode(colRect);
repaintFlowThreadContentRectangle(repaintRect, flowThreadPortion, colRect.location(), &flowThreadOverflowPortion);
}
}
LayoutUnit RenderMultiColumnSet::initialBlockOffsetForPainting() const
{
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool progressionIsInline = multiColumnFlowThread()->progressionIsInline();
LayoutUnit result = 0;
if (!progressionIsInline && progressionReversed) {
LayoutRect colRect = columnRectAt(0);
result = isHorizontalWritingMode() ? colRect.y() : colRect.x();
}
return result;
}
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();
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool progressionIsInline = multiColumnFlowThread()->progressionIsInline();
LayoutUnit initialBlockOffset = initialBlockOffsetForPainting();
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;
LayoutSize translationOffset;
LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit();
bool leftToRight = style().isLeftToRightDirection() ^ progressionReversed;
if (!leftToRight) {
inlineOffset = -inlineOffset;
if (progressionReversed)
inlineOffset += contentLogicalWidth() - colLogicalWidth;
}
translationOffset.setWidth(inlineOffset);
LayoutUnit blockOffset = initialBlockOffset + logicalTop() - flowThread()->logicalTop() + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x());
if (!progressionIsInline) {
if (!progressionReversed)
blockOffset = i * colGap;
else
blockOffset -= i * (computedColumnHeight() + colGap);
}
if (isFlippedBlocksWritingMode(style().writingMode()))
blockOffset = -blockOffset;
translationOffset.setHeight(blockOffset);
if (!isHorizontalWritingMode())
translationOffset = translationOffset.transposedSize();
LayoutRect translatedDirtyRect(dirtyRect);
translatedDirtyRect.move(-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);
}
}
LayoutPoint RenderMultiColumnSet::columnTranslationForOffset(const LayoutUnit& offset) const
{
unsigned startColumn = columnIndexAtOffset(offset);
LayoutUnit colGap = columnGap();
LayoutRect flowThreadPortion = flowThreadPortionRectAt(startColumn);
LayoutPoint translationOffset;
bool progressionReversed = multiColumnFlowThread()->progressionIsReversed();
bool progressionIsInline = multiColumnFlowThread()->progressionIsInline();
LayoutUnit initialBlockOffset = initialBlockOffsetForPainting();
translationOffset.setX(columnLogicalLeft(startColumn));
LayoutUnit blockOffset = initialBlockOffset - (isHorizontalWritingMode() ? flowThreadPortion.y() : flowThreadPortion.x());
if (!progressionIsInline) {
if (!progressionReversed)
blockOffset = startColumn * colGap;
else
blockOffset -= startColumn * (computedColumnHeight() + colGap);
}
if (isFlippedBlocksWritingMode(style().writingMode()))
blockOffset = -blockOffset;
translationOffset.setY(blockOffset);
if (!isHorizontalWritingMode())
translationOffset = translationOffset.transposedPoint();
return translationOffset;
}
void RenderMultiColumnSet::adjustRegionBoundsFromFlowThreadPortionRect(const LayoutPoint&, LayoutRect&)
{
ASSERT_NOT_REACHED();
}
void RenderMultiColumnSet::addOverflowFromChildren()
{
unsigned colCount = columnCount();
if (!colCount)
return;
LayoutRect lastRect = columnRectAt(colCount - 1);
addLayoutOverflow(lastRect);
if (!hasOverflowClip())
addVisualOverflow(lastRect);
}
VisiblePosition RenderMultiColumnSet::positionForPoint(const LayoutPoint& logicalPoint, const RenderRegion*)
{
return multiColumnFlowThread()->positionForPoint(translateRegionPointToFlowThread(logicalPoint, ClampHitTestTranslationToColumns), this);
}
LayoutPoint RenderMultiColumnSet::translateRegionPointToFlowThread(const LayoutPoint & logicalPoint, ColumnHitTestTranslationMode clampMode) const
{
LayoutUnit colGap = columnGap();
LayoutUnit halfColGap = colGap / 2;
bool progressionIsInline = multiColumnFlowThread()->progressionIsInline();
LayoutPoint point = logicalPoint;
for (unsigned i = 0; i < columnCount(); i++) {
LayoutRect colRect = columnRectAt(i);
if (isHorizontalWritingMode() == progressionIsInline) {
LayoutRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height());
if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) {
if (clampMode == ClampHitTestTranslationToColumns) {
if (progressionIsInline) {
if (point.y() < gapAndColumnRect.y())
point = gapAndColumnRect.location();
else if (point.y() >= gapAndColumnRect.maxY()) {
point = gapAndColumnRect.location();
point.move(0, gapAndColumnRect.height());
}
} else {
if (point.x() < colRect.x())
point.setX(colRect.x());
else if (point.x() >= colRect.maxX())
point.setX(colRect.maxX() - 1);
}
}
LayoutSize offsetInColumn = point - colRect.location();
LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
return flowThreadPortion.location() + offsetInColumn;
}
} else {
LayoutRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap);
if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) {
if (clampMode == ClampHitTestTranslationToColumns) {
if (progressionIsInline) {
if (point.x() < gapAndColumnRect.x())
point = gapAndColumnRect.location();
else if (point.x() >= gapAndColumnRect.maxX()) {
point = gapAndColumnRect.location();
point.move(gapAndColumnRect.width(), 0);
}
} else {
if (point.y() < colRect.y())
point.setY(colRect.y());
else if (point.y() >= colRect.maxY())
point.setY(colRect.maxY() - 1);
}
}
LayoutSize offsetInColumn = point - colRect.location();
LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
return flowThreadPortion.location() + offsetInColumn;
}
}
}
return logicalPoint;
}
void RenderMultiColumnSet::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
{
if (result.innerNode() || !parent()->isRenderView())
return;
Node* node = document().documentElement();
if (node) {
result.setInnerNode(node);
if (!result.innerNonSharedNode())
result.setInnerNonSharedNode(node);
LayoutPoint adjustedPoint = translateRegionPointToFlowThread(point);
view().offsetForContents(adjustedPoint);
result.setLocalPoint(adjustedPoint);
}
}
const char* RenderMultiColumnSet::renderName() const
{
return "RenderMultiColumnSet";
}
}