| 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..9b3e8de3fda26c2bb1947877dc405341a5772b82
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.cpp
|
| @@ -0,0 +1,187 @@
|
| +// 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()
|
| +{
|
| + // This is before any early return to ensure the background obscuration status is saved.
|
| + bool backgroundObscurationChanged = false;
|
| + bool backgroundObscured = m_object.backgroundIsKnownToBeObscured();
|
| + if (backgroundObscured != m_object.previousBackgroundObscured()) {
|
| + m_object.getMutableForPainting().setPreviousBackgroundObscured(backgroundObscured);
|
| + backgroundObscurationChanged = true;
|
| + }
|
| +
|
| + if (m_context.forcedSubtreeInvalidationFlags & PaintInvalidatorContext::ForcedSubtreeFullInvalidation)
|
| + return PaintInvalidationSubtree;
|
| +
|
| + if (m_object.shouldDoFullPaintInvalidation())
|
| + return m_object.fullPaintInvalidationReason();
|
| +
|
| + if (backgroundObscurationChanged)
|
| + 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
|
|
|