Index: Source/core/rendering/RenderBlockFlow.cpp |
diff --git a/Source/core/rendering/RenderBlockFlow.cpp b/Source/core/rendering/RenderBlockFlow.cpp |
index 0513586dd5081be7a3de8f49d36c672de700222b..afc0212fa770522ad2b8b05610560df1286d5192 100644 |
--- a/Source/core/rendering/RenderBlockFlow.cpp |
+++ b/Source/core/rendering/RenderBlockFlow.cpp |
@@ -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) { |