Chromium Code Reviews| Index: third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.cpp |
| diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d2e50c628b73e4ca151596ae94c8e71d7f045c4d |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.cpp |
| @@ -0,0 +1,186 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "core/paint/ObjectPaintInvalidator.h" |
| + |
| +#include "core/layout/LayoutBlockFlow.h" |
| +#include "core/paint/PaintInvalidator.h" |
| +#include "core/paint/PaintLayer.h" |
| + |
| +namespace blink { |
| + |
| +typedef HashMap<const LayoutObject*, LayoutRect> SelectionPaintInvalidationMap; |
| +static SelectionPaintInvalidationMap& selectionPaintInvalidationMap() |
| +{ |
| + DEFINE_STATIC_LOCAL(SelectionPaintInvalidationMap, map, ()); |
| + return map; |
| +} |
| + |
| +static void setPreviousSelectionPaintInvalidationRect(const LayoutObject& object, const LayoutRect& rect) |
| +{ |
| + if (rect.isEmpty()) |
| + selectionPaintInvalidationMap().remove(&object); |
| + else |
| + selectionPaintInvalidationMap().set(&object, rect); |
| +} |
| + |
| +void ObjectPaintInvalidator::objectWillBeDestroyed(const LayoutObject& object) |
| +{ |
| + selectionPaintInvalidationMap().remove(&object); |
| +} |
| + |
| +void ObjectPaintInvalidator::incrementallyInvalidatePaint() |
| +{ |
| + const LayoutRect& oldBounds = m_context.oldBounds; |
| + const LayoutRect& newBounds = m_context.newBounds; |
| + |
| + DCHECK(oldBounds.location() == newBounds.location()); |
| + |
| + LayoutUnit deltaRight = newBounds.maxX() - oldBounds.maxX(); |
| + if (deltaRight > 0) { |
| + LayoutRect invalidationRect(oldBounds.maxX(), newBounds.y(), deltaRight, newBounds.height()); |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, invalidationRect, PaintInvalidationIncremental); |
| + } else if (deltaRight < 0) { |
| + LayoutRect invalidationRect(newBounds.maxX(), oldBounds.y(), -deltaRight, oldBounds.height()); |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, invalidationRect, PaintInvalidationIncremental); |
| + } |
| + |
| + LayoutUnit deltaBottom = newBounds.maxY() - oldBounds.maxY(); |
| + if (deltaBottom > 0) { |
| + LayoutRect invalidationRect(newBounds.x(), oldBounds.maxY(), newBounds.width(), deltaBottom); |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, invalidationRect, PaintInvalidationIncremental); |
| + } else if (deltaBottom < 0) { |
| + LayoutRect invalidationRect(oldBounds.x(), newBounds.maxY(), oldBounds.width(), -deltaBottom); |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, invalidationRect, PaintInvalidationIncremental); |
| + } |
| +} |
| + |
| +void ObjectPaintInvalidator::fullyInvalidatePaint(PaintInvalidationReason reason, const LayoutRect& oldBounds, const LayoutRect& newBounds) |
| +{ |
| + // The following logic avoids invalidating twice if one set of bounds contains the other. |
| + if (!newBounds.contains(oldBounds)) { |
| + LayoutRect invalidationRect = oldBounds; |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, invalidationRect, reason); |
| + |
| + if (invalidationRect.contains(newBounds)) |
| + return; |
| + } |
| + |
| + m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, newBounds, reason); |
| +} |
| + |
| +PaintInvalidationReason ObjectPaintInvalidator::computePaintInvalidationReason() |
| +{ |
| + bool boxDecorationBackgroundObscurationChanged = false; |
|
chrishtr
2016/08/09 23:47:31
75-80 are new code?
Xianzhu
2016/08/10 16:25:01
They are previously in LayoutObject::invalidatePai
|
| + bool boxDecorationBackgroundObscured = m_object.boxDecorationBackgroundIsKnownToBeObscured(); |
| + if (boxDecorationBackgroundObscured != m_object.previousBoxDecorationBackgroundObscured()) { |
| + m_object.getMutableForPainting().setPreviousBoxDecorationBackgroundObscured(boxDecorationBackgroundObscured); |
| + boxDecorationBackgroundObscurationChanged = true; |
| + } |
| + |
| + if (m_context.forcedSubtreeInvalidationFlags & PaintInvalidatorContext::ForcedSubtreeFullInvalidation) |
| + return PaintInvalidationSubtree; |
| + |
| + if (m_object.shouldDoFullPaintInvalidation()) |
| + return m_object.fullPaintInvalidationReason(); |
| + |
| + if (boxDecorationBackgroundObscurationChanged) |
| + return PaintInvalidationBackgroundObscurationChange; |
| + |
| + if (m_object.paintedOutputOfObjectHasNoEffect()) |
| + return PaintInvalidationNone; |
| + |
| + const ComputedStyle& style = m_object.styleRef(); |
| + |
| + // The outline may change shape because of position change of descendants. For simplicity, |
| + // just force full paint invalidation if this object is marked for checking paint invalidation |
| + // for any reason. |
| + // TODO(wangxianzhu): Optimize this. |
| + if (style.hasOutline()) |
| + return PaintInvalidationOutline; |
| + |
| + bool locationChanged = m_context.newLocation != m_context.oldLocation; |
| + |
| + // If the bounds are the same then we know that none of the statements below |
| + // can match, so we can early out. |
| + if (m_context.oldBounds == m_context.newBounds) |
| + return locationChanged && !m_context.oldBounds.isEmpty() ? PaintInvalidationLocationChange : PaintInvalidationNone; |
| + |
| + // If we shifted, we don't know the exact reason so we are conservative and trigger a full invalidation. Shifting could |
| + // be caused by some layout property (left / top) or some in-flow layoutObject inserted / removed before us in the tree. |
| + if (m_context.newBounds.location() != m_context.oldBounds.location()) |
| + return PaintInvalidationBoundsChange; |
| + |
| + // If the size is zero on one of our bounds then we know we're going to have |
| + // to do a full invalidation of either old bounds or new bounds. |
| + if (m_context.oldBounds.isEmpty()) |
| + return PaintInvalidationBecameVisible; |
| + if (m_context.newBounds.isEmpty()) |
| + return PaintInvalidationBecameInvisible; |
| + |
| + if (locationChanged) |
| + return PaintInvalidationLocationChange; |
| + |
| + return PaintInvalidationIncremental; |
| +} |
| + |
| +void ObjectPaintInvalidator::invalidateSelectionIfNeeded(PaintInvalidationReason reason) |
| +{ |
| + // Update selection rect when we are doing full invalidation (in case that the object is moved, |
| + // composite status changed, etc.) or shouldInvalidationSelection is set (in case that the |
| + // selection itself changed). |
| + bool fullInvalidation = isFullPaintInvalidationReason(reason); |
| + if (!fullInvalidation && !m_object.shouldInvalidateSelection()) |
| + return; |
| + |
| + LayoutRect oldSelectionRect = selectionPaintInvalidationMap().get(&m_object); |
| + LayoutRect newSelectionRect = m_object.localSelectionRect(); |
| + if (!newSelectionRect.isEmpty()) |
| + m_context.mapLocalRectToPaintInvalidationBacking(m_object, newSelectionRect); |
| + |
| + newSelectionRect.move(m_object.scrollAdjustmentForPaintInvalidation(*m_context.paintInvalidationContainer)); |
| + |
| + setPreviousSelectionPaintInvalidationRect(m_object, newSelectionRect); |
| + |
| + if (!fullInvalidation) { |
| + fullyInvalidatePaint(PaintInvalidationSelection, oldSelectionRect, newSelectionRect); |
| + m_context.paintingLayer->setNeedsRepaint(); |
| + m_object.invalidateDisplayItemClients(PaintInvalidationSelection); |
| + } |
| +} |
| + |
| +PaintInvalidationReason ObjectPaintInvalidator::invalidatePaintIfNeededWithComputedReason(PaintInvalidationReason reason) |
| +{ |
| + // We need to invalidate the selection before checking for whether we are doing a full invalidation. |
| + // This is because we need to update the previous selection rect regardless. |
| + invalidateSelectionIfNeeded(reason); |
| + |
| + switch (reason) { |
| + case PaintInvalidationNone: |
| + // TODO(trchen): Currently we don't keep track of paint offset of layout objects. |
| + // There are corner cases that the display items need to be invalidated for paint offset |
| + // mutation, but incurs no pixel difference (i.e. bounds stay the same) so no rect-based |
| + // invalidation is issued. See crbug.com/508383 and crbug.com/515977. |
| + // This is a workaround to force display items to update paint offset. |
| + if (m_context.forcedSubtreeInvalidationFlags & PaintInvalidatorContext::ForcedSubtreeInvalidationChecking) { |
| + reason = PaintInvalidationLocationChange; |
| + break; |
| + } |
| + return PaintInvalidationNone; |
| + case PaintInvalidationIncremental: |
| + incrementallyInvalidatePaint(); |
| + break; |
| + case PaintInvalidationDelayedFull: |
| + return PaintInvalidationDelayedFull; |
| + default: |
| + DCHECK(isFullPaintInvalidationReason(reason)); |
| + fullyInvalidatePaint(reason, m_context.oldBounds, m_context.newBounds); |
| + } |
| + |
| + m_context.paintingLayer->setNeedsRepaint(); |
| + m_object.invalidateDisplayItemClients(reason); |
| + return reason; |
| +} |
| + |
| +} // namespace blink |