Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1595)

Unified Diff: Source/core/rendering/RenderBlockFlow.cpp

Issue 720313002: Push selection gaps logic down to RenderBlockFlow (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: move even MOAR! Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/rendering/RenderBlockFlow.h ('k') | Source/core/rendering/RenderLayer.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« no previous file with comments | « Source/core/rendering/RenderBlockFlow.h ('k') | Source/core/rendering/RenderLayer.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698