OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/paint/ObjectPaintInvalidator.h" |
| 6 |
| 7 #include "core/layout/LayoutBlockFlow.h" |
| 8 #include "core/paint/PaintInvalidator.h" |
| 9 #include "core/paint/PaintLayer.h" |
| 10 |
| 11 namespace blink { |
| 12 |
| 13 typedef HashMap<const LayoutObject*, LayoutRect> SelectionPaintInvalidationMap; |
| 14 static SelectionPaintInvalidationMap& selectionPaintInvalidationMap() |
| 15 { |
| 16 DEFINE_STATIC_LOCAL(SelectionPaintInvalidationMap, map, ()); |
| 17 return map; |
| 18 } |
| 19 |
| 20 static void setPreviousSelectionPaintInvalidationRect(const LayoutObject& object
, const LayoutRect& rect) |
| 21 { |
| 22 if (rect.isEmpty()) |
| 23 selectionPaintInvalidationMap().remove(&object); |
| 24 else |
| 25 selectionPaintInvalidationMap().set(&object, rect); |
| 26 } |
| 27 |
| 28 void ObjectPaintInvalidator::objectWillBeDestroyed(const LayoutObject& object) |
| 29 { |
| 30 selectionPaintInvalidationMap().remove(&object); |
| 31 } |
| 32 |
| 33 void ObjectPaintInvalidator::incrementallyInvalidatePaint() |
| 34 { |
| 35 const LayoutRect& oldBounds = m_context.oldBounds; |
| 36 const LayoutRect& newBounds = m_context.newBounds; |
| 37 |
| 38 DCHECK(oldBounds.location() == newBounds.location()); |
| 39 |
| 40 LayoutUnit deltaRight = newBounds.maxX() - oldBounds.maxX(); |
| 41 if (deltaRight > 0) { |
| 42 LayoutRect invalidationRect(oldBounds.maxX(), newBounds.y(), deltaRight,
newBounds.height()); |
| 43 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationConta
iner, invalidationRect, PaintInvalidationIncremental); |
| 44 } else if (deltaRight < 0) { |
| 45 LayoutRect invalidationRect(newBounds.maxX(), oldBounds.y(), -deltaRight
, oldBounds.height()); |
| 46 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationConta
iner, invalidationRect, PaintInvalidationIncremental); |
| 47 } |
| 48 |
| 49 LayoutUnit deltaBottom = newBounds.maxY() - oldBounds.maxY(); |
| 50 if (deltaBottom > 0) { |
| 51 LayoutRect invalidationRect(newBounds.x(), oldBounds.maxY(), newBounds.w
idth(), deltaBottom); |
| 52 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationConta
iner, invalidationRect, PaintInvalidationIncremental); |
| 53 } else if (deltaBottom < 0) { |
| 54 LayoutRect invalidationRect(oldBounds.x(), newBounds.maxY(), oldBounds.w
idth(), -deltaBottom); |
| 55 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationConta
iner, invalidationRect, PaintInvalidationIncremental); |
| 56 } |
| 57 } |
| 58 |
| 59 void ObjectPaintInvalidator::fullyInvalidatePaint(PaintInvalidationReason reason
, const LayoutRect& oldBounds, const LayoutRect& newBounds) |
| 60 { |
| 61 // The following logic avoids invalidating twice if one set of bounds contai
ns the other. |
| 62 if (!newBounds.contains(oldBounds)) { |
| 63 LayoutRect invalidationRect = oldBounds; |
| 64 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationConta
iner, invalidationRect, reason); |
| 65 |
| 66 if (invalidationRect.contains(newBounds)) |
| 67 return; |
| 68 } |
| 69 |
| 70 m_object.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer
, newBounds, reason); |
| 71 } |
| 72 |
| 73 PaintInvalidationReason ObjectPaintInvalidator::computePaintInvalidationReason() |
| 74 { |
| 75 // This is before any early return to ensure the background obscuration stat
us is saved. |
| 76 bool backgroundObscurationChanged = false; |
| 77 bool backgroundObscured = m_object.backgroundIsKnownToBeObscured(); |
| 78 if (backgroundObscured != m_object.previousBackgroundObscured()) { |
| 79 m_object.getMutableForPainting().setPreviousBackgroundObscured(backgroun
dObscured); |
| 80 backgroundObscurationChanged = true; |
| 81 } |
| 82 |
| 83 if (m_context.forcedSubtreeInvalidationFlags & PaintInvalidatorContext::Forc
edSubtreeFullInvalidation) |
| 84 return PaintInvalidationSubtree; |
| 85 |
| 86 if (m_object.shouldDoFullPaintInvalidation()) |
| 87 return m_object.fullPaintInvalidationReason(); |
| 88 |
| 89 if (backgroundObscurationChanged) |
| 90 return PaintInvalidationBackgroundObscurationChange; |
| 91 |
| 92 if (m_object.paintedOutputOfObjectHasNoEffect()) |
| 93 return PaintInvalidationNone; |
| 94 |
| 95 const ComputedStyle& style = m_object.styleRef(); |
| 96 |
| 97 // The outline may change shape because of position change of descendants. F
or simplicity, |
| 98 // just force full paint invalidation if this object is marked for checking
paint invalidation |
| 99 // for any reason. |
| 100 // TODO(wangxianzhu): Optimize this. |
| 101 if (style.hasOutline()) |
| 102 return PaintInvalidationOutline; |
| 103 |
| 104 bool locationChanged = m_context.newLocation != m_context.oldLocation; |
| 105 |
| 106 // If the bounds are the same then we know that none of the statements below |
| 107 // can match, so we can early out. |
| 108 if (m_context.oldBounds == m_context.newBounds) |
| 109 return locationChanged && !m_context.oldBounds.isEmpty() ? PaintInvalida
tionLocationChange : PaintInvalidationNone; |
| 110 |
| 111 // If we shifted, we don't know the exact reason so we are conservative and
trigger a full invalidation. Shifting could |
| 112 // be caused by some layout property (left / top) or some in-flow layoutObje
ct inserted / removed before us in the tree. |
| 113 if (m_context.newBounds.location() != m_context.oldBounds.location()) |
| 114 return PaintInvalidationBoundsChange; |
| 115 |
| 116 // If the size is zero on one of our bounds then we know we're going to have |
| 117 // to do a full invalidation of either old bounds or new bounds. |
| 118 if (m_context.oldBounds.isEmpty()) |
| 119 return PaintInvalidationBecameVisible; |
| 120 if (m_context.newBounds.isEmpty()) |
| 121 return PaintInvalidationBecameInvisible; |
| 122 |
| 123 if (locationChanged) |
| 124 return PaintInvalidationLocationChange; |
| 125 |
| 126 return PaintInvalidationIncremental; |
| 127 } |
| 128 |
| 129 void ObjectPaintInvalidator::invalidateSelectionIfNeeded(PaintInvalidationReason
reason) |
| 130 { |
| 131 // Update selection rect when we are doing full invalidation (in case that t
he object is moved, |
| 132 // composite status changed, etc.) or shouldInvalidationSelection is set (in
case that the |
| 133 // selection itself changed). |
| 134 bool fullInvalidation = isFullPaintInvalidationReason(reason); |
| 135 if (!fullInvalidation && !m_object.shouldInvalidateSelection()) |
| 136 return; |
| 137 |
| 138 LayoutRect oldSelectionRect = selectionPaintInvalidationMap().get(&m_object)
; |
| 139 LayoutRect newSelectionRect = m_object.localSelectionRect(); |
| 140 if (!newSelectionRect.isEmpty()) |
| 141 m_context.mapLocalRectToPaintInvalidationBacking(m_object, newSelectionR
ect); |
| 142 |
| 143 newSelectionRect.move(m_object.scrollAdjustmentForPaintInvalidation(*m_conte
xt.paintInvalidationContainer)); |
| 144 |
| 145 setPreviousSelectionPaintInvalidationRect(m_object, newSelectionRect); |
| 146 |
| 147 if (!fullInvalidation) { |
| 148 fullyInvalidatePaint(PaintInvalidationSelection, oldSelectionRect, newSe
lectionRect); |
| 149 m_context.paintingLayer->setNeedsRepaint(); |
| 150 m_object.invalidateDisplayItemClients(PaintInvalidationSelection); |
| 151 } |
| 152 } |
| 153 |
| 154 PaintInvalidationReason ObjectPaintInvalidator::invalidatePaintIfNeededWithCompu
tedReason(PaintInvalidationReason reason) |
| 155 { |
| 156 // We need to invalidate the selection before checking for whether we are do
ing a full invalidation. |
| 157 // This is because we need to update the previous selection rect regardless. |
| 158 invalidateSelectionIfNeeded(reason); |
| 159 |
| 160 switch (reason) { |
| 161 case PaintInvalidationNone: |
| 162 // TODO(trchen): Currently we don't keep track of paint offset of layout
objects. |
| 163 // There are corner cases that the display items need to be invalidated
for paint offset |
| 164 // mutation, but incurs no pixel difference (i.e. bounds stay the same)
so no rect-based |
| 165 // invalidation is issued. See crbug.com/508383 and crbug.com/515977. |
| 166 // This is a workaround to force display items to update paint offset. |
| 167 if (m_context.forcedSubtreeInvalidationFlags & PaintInvalidatorContext::
ForcedSubtreeInvalidationChecking) { |
| 168 reason = PaintInvalidationLocationChange; |
| 169 break; |
| 170 } |
| 171 return PaintInvalidationNone; |
| 172 case PaintInvalidationIncremental: |
| 173 incrementallyInvalidatePaint(); |
| 174 break; |
| 175 case PaintInvalidationDelayedFull: |
| 176 return PaintInvalidationDelayedFull; |
| 177 default: |
| 178 DCHECK(isFullPaintInvalidationReason(reason)); |
| 179 fullyInvalidatePaint(reason, m_context.oldBounds, m_context.newBounds); |
| 180 } |
| 181 |
| 182 m_context.paintingLayer->setNeedsRepaint(); |
| 183 m_object.invalidateDisplayItemClients(reason); |
| 184 return reason; |
| 185 } |
| 186 |
| 187 } // namespace blink |
OLD | NEW |