Commit 4657fd08 authored by leviw@chromium.org's avatar leviw@chromium.org

Push selection gaps logic down to RenderBlockFlow

Part of my ultimate goal of de-coupling invalidation and painting of selection
gaps from one function so I can move the painting into BlockFlowPainter.

We don't actually paint selection gaps for non-blockFlow RenderBlocks. Make the
code actually represent that!

Review URL: https://codereview.chromium.org/720313002

git-svn-id: svn://svn.chromium.org/blink/trunk@185396 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent a47a6006
......@@ -8,6 +8,7 @@
#include "core/rendering/FloatingObjects.h"
#include "core/rendering/PaintInfo.h"
#include "core/rendering/RenderBlockFlow.h"
#include "core/rendering/RenderLayer.h"
namespace blink {
......@@ -44,4 +45,28 @@ void BlockFlowPainter::paintFloats(PaintInfo& paintInfo, const LayoutPoint& pain
}
}
void BlockFlowPainter::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (m_renderBlockFlow.shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) {
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = m_renderBlockFlow.logicalLeftSelectionOffset(&m_renderBlockFlow, lastTop);
LayoutUnit lastRight = m_renderBlockFlow.logicalRightSelectionOffset(&m_renderBlockFlow, lastTop);
GraphicsContextStateSaver stateSaver(*paintInfo.context);
LayoutRect gapRectsBounds = m_renderBlockFlow.selectionGaps(&m_renderBlockFlow, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo);
if (!gapRectsBounds.isEmpty()) {
RenderLayer* layer = m_renderBlockFlow.enclosingLayer();
gapRectsBounds.moveBy(-paintOffset);
if (!m_renderBlockFlow.hasLayer()) {
LayoutRect localBounds(gapRectsBounds);
m_renderBlockFlow.flipForWritingMode(localBounds);
gapRectsBounds = m_renderBlockFlow.localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox();
if (layer->renderer()->hasOverflowClip())
gapRectsBounds.move(layer->renderBox()->scrolledContentOffset());
}
layer->addBlockSelectionGapsBounds(gapRectsBounds);
}
}
}
} // namespace blink
......@@ -15,7 +15,7 @@ class BlockFlowPainter {
public:
BlockFlowPainter(RenderBlockFlow& renderBlockFlow) : m_renderBlockFlow(renderBlockFlow) { }
void paintFloats(PaintInfo&, const LayoutPoint&, bool preservePhase);
void paintSelection(PaintInfo&, const LayoutPoint&);
private:
RenderBlockFlow& m_renderBlockFlow;
};
......
......@@ -196,7 +196,7 @@ void BlockPainter::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOff
// FIXME: Make this work with multi column layouts. For now don't fill gaps.
bool isPrinting = m_renderBlock.document().printing();
if (!isPrinting && !m_renderBlock.hasColumns())
paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks.
m_renderBlock.paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks.
// 4. paint floats.
if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) {
......@@ -441,30 +441,6 @@ void BlockPainter::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintO
}
}
void BlockPainter::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (m_renderBlock.shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) {
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = m_renderBlock.logicalLeftSelectionOffset(&m_renderBlock, lastTop);
LayoutUnit lastRight = m_renderBlock.logicalRightSelectionOffset(&m_renderBlock, lastTop);
GraphicsContextStateSaver stateSaver(*paintInfo.context);
LayoutRect gapRectsBounds = m_renderBlock.selectionGaps(&m_renderBlock, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo);
if (!gapRectsBounds.isEmpty()) {
RenderLayer* layer = m_renderBlock.enclosingLayer();
gapRectsBounds.moveBy(-paintOffset);
if (!m_renderBlock.hasLayer()) {
LayoutRect localBounds(gapRectsBounds);
m_renderBlock.flipForWritingMode(localBounds);
gapRectsBounds = m_renderBlock.localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox();
if (layer->renderer()->hasOverflowClip())
gapRectsBounds.move(layer->renderBox()->scrolledContentOffset());
}
layer->addBlockSelectionGapsBounds(gapRectsBounds);
}
}
}
void BlockPainter::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderInline* inlineCont = m_renderBlock.inlineElementContinuation();
......
......@@ -239,14 +239,6 @@ void RenderBlock::destroy()
#endif
}
LayoutRect RenderBlock::selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
{
LayoutRect rect = selectionGapRectsForPaintInvalidation(paintInvalidationContainer);
// FIXME: groupedMapping() leaks the squashing abstraction.
if (paintInvalidationContainer->layer()->groupedMapping())
RenderLayer::mapRectToPaintBackingCoordinates(paintInvalidationContainer, rect);
return rect;
}
void RenderBlock::willBeDestroyed()
{
// Mark as being destroyed to avoid trouble with merges in removeChild().
......@@ -1837,11 +1829,6 @@ void RenderBlock::addContinuationWithOutline(RenderInline* flow)
continuations->add(flow);
}
bool RenderBlock::shouldPaintSelectionGaps() const
{
return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
}
bool RenderBlock::isSelectionRoot() const
{
if (isPseudoElement())
......@@ -1868,39 +1855,6 @@ bool RenderBlock::isSelectionRoot() const
return false;
}
GapRects RenderBlock::selectionGapRectsForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
{
ASSERT(!needsLayout());
if (!shouldPaintSelectionGaps())
return GapRects();
TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
mapLocalToContainer(paintInvalidationContainer, transformState, ApplyContainerFlip | UseTransforms);
LayoutPoint offsetFromPaintInvalidationContainer = roundedLayoutPoint(transformState.mappedPoint());
if (hasOverflowClip())
offsetFromPaintInvalidationContainer -= scrolledContentOffset();
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
return selectionGaps(this, offsetFromPaintInvalidationContainer, IntSize(), lastTop, lastLeft, lastRight);
}
static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects)
{
if (!positionedObjects)
return;
TrackedRendererListHashSet::const_iterator end = positionedObjects->end();
for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
RenderBox* r = *it;
paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
}
}
LayoutUnit RenderBlock::blockDirectionOffset(const LayoutSize& offsetFromBlock) const
{
return isHorizontalWritingMode() ? offsetFromBlock.height() : offsetFromBlock.width();
......@@ -1923,192 +1877,6 @@ LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPh
return result;
}
GapRects RenderBlock::selectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) const
{
// IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
// Clip out floating and positioned objects when painting selection gaps.
if (paintInfo) {
// Note that we don't clip out overflow for positioned objects. We just stick to the border box.
LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
rootBlock->flipForWritingMode(flippedBlockRect);
flippedBlockRect.moveBy(rootBlockPhysicalPosition);
clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects());
if (isBody() || isDocumentElement()) // The <body> must make sure to examine its containingBlock's positioned objects.
for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock())
clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes.
clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
}
// FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is
// fixed).
GapRects result;
if (!isRenderBlockFlow()) // FIXME: Make multi-column selection gap filling work someday.
return result;
if (hasColumns() || hasTransformRelatedProperty() || style()->columnSpan()) {
// FIXME: We should learn how to gap fill multiple columns and transforms eventually.
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
return result;
}
if (childrenInline())
result = toRenderBlockFlow(this)->inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
else
result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
// Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd))
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
logicalHeight(), paintInfo));
return result;
}
GapRects RenderBlock::blockSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) const
{
GapRects result;
// Go ahead and jump right to the first block child that contains some selected objects.
RenderBox* curr;
for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
SelectionState childState = curr->selectionState();
if (childState == SelectionBoth || childState == SelectionEnd)
sawSelectionEnd = true;
if (curr->isFloatingOrOutOfFlowPositioned())
continue; // We must be a normal flow object in order to even be considered.
if (curr->isRelPositioned() && curr->hasLayer()) {
// If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
// Just disregard it completely.
LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
if (relOffset.width() || relOffset.height())
continue;
}
bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
if (fillBlockGaps) {
// We need to fill the vertical gap above this object.
if (childState == SelectionEnd || childState == SelectionInside)
// Fill the gap above the object.
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
curr->logicalTop(), paintInfo));
// Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
// our object. We know this if the selection did not end inside our object.
if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
childState = SelectionNone;
// Fill side gaps on this object based off its state.
bool leftGap, rightGap;
getSelectionGapInfo(childState, leftGap, rightGap);
if (leftGap)
result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
if (rightGap)
result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
// Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
// they can without bumping into floating or positioned objects. Ideally they will go right up
// to the border of the root selection block.
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom());
} else if (childState != SelectionNone)
// We must be a block that has some selected object inside it. Go ahead and recur.
result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo));
}
return result;
}
IntRect alignSelectionRectToDevicePixels(LayoutRect& rect)
{
LayoutUnit roundedX = rect.x().round();
return IntRect(roundedX, rect.y().round(),
(rect.maxX() - roundedX).round(),
snapSizeToPixel(rect.height(), rect.y()));
}
LayoutRect RenderBlock::blockSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) const
{
LayoutUnit logicalTop = lastLogicalTop;
LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop;
if (logicalHeight <= 0)
return LayoutRect();
// Get the selection offsets for the bottom of the gap
LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalWidth = logicalRight - logicalLeft;
if (logicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selectionBackgroundColor());
}
return gapRect;
}
LayoutRect RenderBlock::logicalLeftSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) const
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalRight = std::min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalLeft, std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selObj->selectionBackgroundColor());
}
return gapRect;
}
LayoutRect RenderBlock::logicalRightSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) const
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = std::max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalRight, max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selObj->selectionBackgroundColor());
}
return gapRect;
}
void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) const
{
bool ltr = style()->isLeftToRightDirection();
leftGap = (state == RenderObject::SelectionInside) ||
(state == RenderObject::SelectionEnd && ltr) ||
(state == RenderObject::SelectionStart && !ltr);
rightGap = (state == RenderObject::SelectionInside) ||
(state == RenderObject::SelectionStart && ltr) ||
(state == RenderObject::SelectionEnd && !ltr);
}
LayoutUnit RenderBlock::logicalLeftSelectionOffset(const RenderBlock* rootBlock, LayoutUnit position) const
{
// The border can potentially be further extended by our containingBlock().
......
......@@ -158,13 +158,6 @@ public:
LayoutUnit blockDirectionOffset(const LayoutSize& offsetFromBlock) const;
LayoutUnit inlineDirectionOffset(const LayoutSize& offsetFromBlock) const;
virtual bool shouldPaintSelectionGaps() const override final;
GapRects selectionGapRectsForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const;
LayoutRect logicalLeftSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*) const;
LayoutRect logicalRightSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*) const;
void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap) const;
RenderBlock* blockBeforeWithinSelectionRoot(LayoutSize& offset) const;
virtual void setSelectionState(SelectionState) override;
......@@ -242,9 +235,6 @@ public:
virtual LayoutUnit logicalLeftSelectionOffset(const RenderBlock* rootBlock, LayoutUnit position) const;
virtual LayoutUnit logicalRightSelectionOffset(const RenderBlock* rootBlock, LayoutUnit position) const;
GapRects selectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* = 0) const;
#if ENABLE(ASSERT)
void checkPositionedObjectsNeedLayout();
bool paintsContinuationOutline(RenderInline* flow);
......@@ -256,8 +246,6 @@ public:
bool recalcChildOverflowAfterStyleChange();
bool recalcOverflowAfterStyleChange();
virtual LayoutRect selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const override final;
protected:
virtual void willBeDestroyed() override;
......@@ -286,6 +274,7 @@ public:
// FIXME-BLOCKFLOW: Remove virtualizaion when all callers have moved to RenderBlockFlow
virtual void paintFloats(PaintInfo&, const LayoutPoint&, bool) { }
virtual void paintSelection(PaintInfo&, const LayoutPoint&) { }
protected:
virtual void adjustInlineDirectionLineBounds(unsigned /* expansionOpportunityCount */, float& /* logicalLeft */, float& /* logicalWidth */) const { }
......@@ -395,10 +384,6 @@ private:
virtual void childBecameNonInline(RenderObject* child) override final;
bool isSelectionRoot() const;
GapRects blockSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*) const;
LayoutRect blockSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo*) const;
// FIXME-BLOCKFLOW: Remove virtualizaion when all callers have moved to RenderBlockFlow
virtual void clipOutFloatingObjects(const RenderBlock*, const PaintInfo*, const LayoutPoint&, const LayoutSize&) const { };
......
......@@ -37,6 +37,7 @@
#include "core/frame/Settings.h"
#include "core/html/HTMLDialogElement.h"
#include "core/paint/BlockFlowPainter.h"
#include "core/paint/DrawingRecorder.h"
#include "core/rendering/HitTestLocation.h"
#include "core/rendering/RenderFlowThread.h"
#include "core/rendering/RenderLayer.h"
......@@ -46,6 +47,7 @@
#include "core/rendering/RenderView.h"
#include "core/rendering/TextAutosizer.h"
#include "core/rendering/line/LineWidth.h"
#include "platform/geometry/TransformState.h"
#include "platform/text/BidiTextRun.h"
namespace blink {
......@@ -2105,6 +2107,11 @@ void RenderBlockFlow::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paint
BlockFlowPainter(*this).paintFloats(paintInfo, paintOffset, preservePhase);
}
void RenderBlockFlow::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
BlockFlowPainter(*this).paintSelection(paintInfo, paintOffset);
}
void RenderBlockFlow::clipOutFloatingObjects(const RenderBlock* rootBlock, const PaintInfo* paintInfo, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock) const
{
if (m_floatingObjects) {
......@@ -2644,6 +2651,83 @@ LayoutUnit RenderBlockFlow::logicalRightFloatOffsetForLine(LayoutUnit logicalTop
return fixedOffset;
}
LayoutRect RenderBlockFlow::selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
{
LayoutRect rect = selectionGapRectsForPaintInvalidation(paintInvalidationContainer);
// FIXME: groupedMapping() leaks the squashing abstraction.
if (paintInvalidationContainer->layer()->groupedMapping())
RenderLayer::mapRectToPaintBackingCoordinates(paintInvalidationContainer, rect);
return rect;
}
GapRects RenderBlockFlow::selectionGapRectsForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
{
ASSERT(!needsLayout());
if (!shouldPaintSelectionGaps())
return GapRects();
TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
mapLocalToContainer(paintInvalidationContainer, transformState, ApplyContainerFlip | UseTransforms);
LayoutPoint offsetFromPaintInvalidationContainer = roundedLayoutPoint(transformState.mappedPoint());
if (hasOverflowClip())
offsetFromPaintInvalidationContainer -= scrolledContentOffset();
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
return selectionGaps(this, offsetFromPaintInvalidationContainer, IntSize(), lastTop, lastLeft, lastRight);
}
static void clipOutPositionedObjects(const PaintInfo& paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects)
{
if (!positionedObjects)
return;
TrackedRendererListHashSet::const_iterator end = positionedObjects->end();
for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
RenderBox* r = *it;
paintInfo.context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
}
}
GapRects RenderBlockFlow::selectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) const
{
// IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
if (paintInfo) {
// Note that we don't clip out overflow for positioned objects. We just stick to the border box.
LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
rootBlock->flipForWritingMode(flippedBlockRect);
flippedBlockRect.moveBy(rootBlockPhysicalPosition);
clipOutPositionedObjects(*paintInfo, flippedBlockRect.location(), positionedObjects());
if (isBody() || isDocumentElement()) // The <body> must make sure to examine its containingBlock's positioned objects.
for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock())
clipOutPositionedObjects(*paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes.
clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
}
GapRects result;
if (hasColumns() || hasTransformRelatedProperty() || style()->columnSpan())
return result;
if (childrenInline())
result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
else
result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
// Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) {
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), paintInfo));
}
return result;
}
GapRects RenderBlockFlow::inlineSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) const
{
......@@ -2700,6 +2784,156 @@ GapRects RenderBlockFlow::inlineSelectionGaps(const RenderBlock* rootBlock, cons
return result;
}
IntRect alignSelectionRectToDevicePixels(LayoutRect& rect)
{
LayoutUnit roundedX = rect.x().round();
return IntRect(roundedX, rect.y().round(),
(rect.maxX() - roundedX).round(),
snapSizeToPixel(rect.height(), rect.y()));
}
bool RenderBlockFlow::shouldPaintSelectionGaps() const
{
return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
}
LayoutRect RenderBlockFlow::blockSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) const
{
LayoutUnit logicalTop = lastLogicalTop;
LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop;
if (logicalHeight <= 0)
return LayoutRect();
// Get the selection offsets for the bottom of the gap
LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalWidth = logicalRight - logicalLeft;
if (logicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selectionBackgroundColor());
}
return gapRect;
}
GapRects RenderBlockFlow::blockSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) const
{
GapRects result;
// Go ahead and jump right to the first block child that contains some selected objects.
RenderBox* curr;
for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
SelectionState childState = curr->selectionState();
if (childState == SelectionBoth || childState == SelectionEnd)
sawSelectionEnd = true;
if (curr->isFloatingOrOutOfFlowPositioned())
continue; // We must be a normal flow object in order to even be considered.
if (curr->isRelPositioned() && curr->hasLayer()) {
// If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
// Just disregard it completely.
LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
if (relOffset.width() || relOffset.height())
continue;
}
bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
if (fillBlockGaps) {
// We need to fill the vertical gap above this object.
if (childState == SelectionEnd || childState == SelectionInside) {
// Fill the gap above the object.
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
curr->logicalTop(), paintInfo));
}
// Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
// our object. We know this if the selection did not end inside our object.
if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
childState = SelectionNone;
// Fill side gaps on this object based off its state.
bool leftGap, rightGap;
getSelectionGapInfo(childState, leftGap, rightGap);
if (leftGap)
result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
if (rightGap)
result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
// Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
// they can without bumping into floating or positioned objects. Ideally they will go right up
// to the border of the root selection block.
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom());
} else if (childState != SelectionNone && curr->isRenderBlockFlow()) {
// We must be a block that has some selected object inside it. Go ahead and recur.
result.unite(toRenderBlockFlow(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo));
}
}
return result;
}
LayoutRect RenderBlockFlow::logicalLeftSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) const
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalRight = std::min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalLeft, std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selObj->selectionBackgroundColor());
}
return gapRect;
}
LayoutRect RenderBlockFlow::logicalRightSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) const
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = std::max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalRight, max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo) {
IntRect selectionGapRect = alignSelectionRectToDevicePixels(gapRect);
DrawingRecorder recorder(paintInfo->context, this, paintInfo->phase, selectionGapRect);
paintInfo->context->fillRect(selectionGapRect, selObj->selectionBackgroundColor());
}
return gapRect;
}
void RenderBlockFlow::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) const
{
bool ltr = style()->isLeftToRightDirection();
leftGap = (state == RenderObject::SelectionInside)
|| (state == RenderObject::SelectionEnd && ltr)
|| (state == RenderObject::SelectionStart && !ltr);
rightGap = (state == RenderObject::SelectionInside)
|| (state == RenderObject::SelectionStart && ltr)
|| (state == RenderObject::SelectionEnd && !ltr);
}
void RenderBlockFlow::setPaginationStrut(LayoutUnit strut)
{
if (!m_rareData) {
......
......@@ -181,8 +181,23 @@ public:
// FIXME: This should be const to avoid a const_cast, but can modify child dirty bits and RenderCombineText
void computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth);
virtual bool shouldPaintSelectionGaps() const override final;
LayoutRect logicalLeftSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*) const;
LayoutRect logicalRightSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
const RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*) const;
void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap) const;
virtual LayoutRect selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const override final;
GapRects selectionGapRectsForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const;
GapRects selectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* = 0) const;
GapRects inlineSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*) const;
GapRects blockSelectionGaps(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*) const;
LayoutRect blockSelectionGap(const RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo*) const;
LayoutUnit paginationStrut() const { return m_rareData ? m_rareData->m_paginationStrut : LayoutUnit(); }
void setPaginationStrut(LayoutUnit);
......@@ -267,6 +282,7 @@ private:
virtual void invalidatePaintForOverhangingFloats(bool paintAllDescendants) override final;
virtual void invalidatePaintForOverflow() override final;
virtual void paintFloats(PaintInfo&, const LayoutPoint&, bool preservePhase = false) override final;
virtual void paintSelection(PaintInfo&, const LayoutPoint&) override final;
virtual void clipOutFloatingObjects(const RenderBlock*, const PaintInfo*, const LayoutPoint&, const LayoutSize&) const override;
void clearFloats(EClear);
......
......@@ -2156,11 +2156,11 @@ void RenderLayer::invalidatePaintForBlockSelectionGaps()
IntRect RenderLayer::blockSelectionGapsBounds() const
{
if (!renderer()->isRenderBlock())
if (!renderer()->isRenderBlockFlow())
return IntRect();
RenderBlock* renderBlock = toRenderBlock(renderer());
LayoutRect gapRects = renderBlock->selectionGapRectsForPaintInvalidation(renderBlock);
RenderBlockFlow* renderBlockFlow = toRenderBlockFlow(renderer());
LayoutRect gapRects = renderBlockFlow->selectionGapRectsForPaintInvalidation(renderBlockFlow);
return pixelSnappedIntRect(gapRects);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment