Chromium Code Reviews| 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/BoxPaintInvalidator.h" | |
| 6 | |
| 7 #include "core/frame/Settings.h" | |
| 8 #include "core/layout/LayoutView.h" | |
| 9 #include "core/paint/ObjectPaintInvalidator.h" | |
| 10 #include "core/paint/PaintInvalidator.h" | |
| 11 #include "core/paint/PaintLayer.h" | |
| 12 #include "core/paint/PaintLayerScrollableArea.h" | |
| 13 #include "platform/geometry/LayoutRect.h" | |
| 14 | |
| 15 namespace blink { | |
| 16 | |
| 17 struct PreviousBoxSizes { | |
| 18 LayoutSize borderBoxSize; | |
| 19 LayoutRect contentBoxRect; | |
| 20 LayoutRect layoutOverflowRect; | |
| 21 }; | |
| 22 | |
| 23 typedef HashMap<const LayoutBox*, PreviousBoxSizes> PreviousBoxSizesMap; | |
| 24 static PreviousBoxSizesMap& previousBoxSizesMap() | |
| 25 { | |
| 26 DEFINE_STATIC_LOCAL(PreviousBoxSizesMap, map, ()); | |
| 27 return map; | |
| 28 } | |
| 29 | |
| 30 void BoxPaintInvalidator::boxWillBeDestroyed(const LayoutBox& box) | |
| 31 { | |
| 32 previousBoxSizesMap().remove(&box); | |
| 33 } | |
| 34 | |
| 35 // This is called when ObjectPaintInvalidator already did incremental invalidati on, | |
| 36 // so this function just does what is additionally needed for the LayoutBox. | |
| 37 void BoxPaintInvalidator::incrementallyInvalidatePaint() | |
| 38 { | |
| 39 bool hasBoxDecorations = m_box.styleRef().hasBoxDecorations(); | |
|
chrishtr
2016/08/09 23:47:31
This used to delegate to LayoutObject first.
Xianzhu
2016/08/10 16:25:01
It doesn't delegate because ObjectPaintInvalidator
| |
| 40 if (!m_box.styleRef().hasBackground() && !hasBoxDecorations) | |
| 41 return; | |
| 42 | |
| 43 const LayoutRect& oldBounds = m_context.oldBounds; | |
| 44 const LayoutRect& newBounds = m_context.newBounds; | |
| 45 | |
| 46 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(oldBounds.size()) ; | |
| 47 LayoutSize newBorderBoxSize = m_box.size(); | |
| 48 | |
| 49 // If border m_box size didn't change, LayoutObject's incrementallyInvalidat ePaint() is good. | |
| 50 if (oldBorderBoxSize == newBorderBoxSize) | |
| 51 return; | |
| 52 | |
| 53 // If size of the paint invalidation rect equals to size of border box, Obje ctPaintInvalidator::incrementallyInvalidatePaint() | |
| 54 // is good for boxes having background without box decorations. | |
| 55 DCHECK(oldBounds.location() == newBounds.location()); // Otherwise we won't do incremental invalidation. | |
| 56 if (!hasBoxDecorations | |
| 57 && m_context.newLocation == newBounds.location() | |
| 58 && oldBorderBoxSize == oldBounds.size() | |
| 59 && newBorderBoxSize == newBounds.size()) | |
| 60 return; | |
| 61 | |
| 62 // Invalidate the right delta part and the right border of the old or new m_ box which has smaller width. | |
| 63 if (LayoutUnit deltaWidth = (oldBorderBoxSize.width() - newBorderBoxSize.wid th()).abs()) { | |
| 64 LayoutUnit smallerWidth = std::min(oldBorderBoxSize.width(), newBorderBo xSize.width()); | |
| 65 LayoutUnit borderTopRightRadiusWidth = valueForLength(m_box.styleRef().b orderTopRightRadius().width(), smallerWidth); | |
| 66 LayoutUnit borderBottomRightRadiusWidth = valueForLength(m_box.styleRef( ).borderBottomRightRadius().width(), smallerWidth); | |
| 67 LayoutUnit borderWidth = std::max(LayoutUnit(m_box.borderRight()), std:: max(borderTopRightRadiusWidth, borderBottomRightRadiusWidth)); | |
| 68 LayoutRect rightDeltaRect(m_context.newLocation.x() + smallerWidth - bor derWidth, m_context.newLocation.y(), | |
| 69 deltaWidth + borderWidth, std::max(oldBorderBoxSize.height(), newBor derBoxSize.height())); | |
| 70 invalidatePaintRectClippedByOldAndNewBounds(rightDeltaRect); | |
| 71 } | |
| 72 | |
| 73 // Invalidate the bottom delta part and the bottom border of the old or new m_box which has smaller height. | |
| 74 if (LayoutUnit deltaHeight = (oldBorderBoxSize.height() - newBorderBoxSize.h eight()).abs()) { | |
| 75 LayoutUnit smallerHeight = std::min(oldBorderBoxSize.height(), newBorder BoxSize.height()); | |
| 76 LayoutUnit borderBottomLeftRadiusHeight = valueForLength(m_box.styleRef( ).borderBottomLeftRadius().height(), smallerHeight); | |
| 77 LayoutUnit borderBottomRightRadiusHeight = valueForLength(m_box.styleRef ().borderBottomRightRadius().height(), smallerHeight); | |
| 78 LayoutUnit borderHeight = std::max(LayoutUnit(m_box.borderBottom()), std ::max(borderBottomLeftRadiusHeight, borderBottomRightRadiusHeight)); | |
| 79 LayoutRect bottomDeltaRect(m_context.newLocation.x(), m_context.newLocat ion.y() + smallerHeight - borderHeight, | |
| 80 std::max(oldBorderBoxSize.width(), newBorderBoxSize.width()), deltaH eight + borderHeight); | |
| 81 invalidatePaintRectClippedByOldAndNewBounds(bottomDeltaRect); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 void BoxPaintInvalidator::invalidatePaintRectClippedByOldAndNewBounds(const Layo utRect& rect) | |
| 86 { | |
| 87 if (rect.isEmpty()) | |
| 88 return; | |
| 89 | |
| 90 LayoutRect rectClippedByOldBounds = intersection(rect, m_context.oldBounds); | |
| 91 LayoutRect rectClippedByNewBounds = intersection(rect, m_context.newBounds); | |
| 92 // Invalidate only once if the clipped rects equal. | |
| 93 if (rectClippedByOldBounds == rectClippedByNewBounds) { | |
| 94 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByOldBounds, PaintInvalidationIncremental); | |
| 95 return; | |
| 96 } | |
| 97 // Invalidate the bigger one if one contains another. Otherwise invalidate b oth. | |
| 98 if (!rectClippedByNewBounds.contains(rectClippedByOldBounds)) | |
| 99 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByOldBounds, PaintInvalidationIncremental); | |
| 100 if (!rectClippedByOldBounds.contains(rectClippedByNewBounds)) | |
| 101 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByNewBounds, PaintInvalidationIncremental); | |
| 102 } | |
| 103 | |
| 104 PaintInvalidationReason BoxPaintInvalidator::computePaintInvalidationReason() | |
| 105 { | |
| 106 PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).co mputePaintInvalidationReason(); | |
| 107 | |
| 108 // If the current paint invalidation reason is PaintInvalidationDelayedFull, then this paint invalidation | |
| 109 // can be delayed if the LayoutBox in question is not on-screen. The logic t o decide whether this is appropriate | |
| 110 // exists at the site of the original paint invalidation that chose PaintInv alidationDelayedFull. | |
| 111 if (reason == PaintInvalidationDelayedFull) { | |
| 112 if (!m_box.intersectsVisibleViewport()) | |
| 113 return PaintInvalidationDelayedFull; | |
| 114 | |
| 115 // Do regular full paint invalidation if the object is on screen. | |
| 116 return PaintInvalidationFull; | |
|
chrishtr
2016/08/09 23:47:31
Previously, in LayoutBox.cpp:1578, we called setSh
Xianzhu
2016/08/10 16:25:01
I have verified the tests and they still work.
Th
| |
| 117 } | |
| 118 | |
| 119 if (isFullPaintInvalidationReason(reason)) | |
| 120 return reason; | |
| 121 | |
| 122 if (m_box.isLayoutView()) { | |
| 123 const LayoutView& layoutView = toLayoutView(m_box); | |
| 124 // In normal compositing mode, root background doesn't need to be invali dated for | |
| 125 // box changes, because the background always covers the whole document rect | |
| 126 // and clipping is done by compositor()->m_containerLayer. Also the scro llbars | |
| 127 // are always composited. There are no other box decoration on the Layou tView thus | |
| 128 // we can safely exit here. | |
| 129 if (layoutView.usesCompositing() && (!layoutView.document().settings() | | !layoutView.document().settings()->rootLayerScrolls())) | |
| 130 return reason; | |
| 131 } | |
| 132 | |
| 133 // If the transform is not identity or translation, incremental invalidation is not applicable | |
| 134 // because the difference between oldBounds and newBounds doesn't cover all area needing invalidation. | |
| 135 // FIXME: Should also consider ancestor transforms since paintInvalidationCo ntainer. crbug.com/426111. | |
| 136 if (reason == PaintInvalidationIncremental | |
| 137 && m_context.paintInvalidationContainer != m_box | |
| 138 && m_box.hasLayer() && m_box.layer()->transform() | |
| 139 && !m_box.layer()->transform()->isIdentityOrTranslation()) | |
| 140 return PaintInvalidationBoundsChange; | |
| 141 | |
| 142 const ComputedStyle& style = m_box.styleRef(); | |
| 143 if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.maskLa yers().thisOrNextLayersUseContentBox() || style.boxSizing() == BoxSizingBorderBo x) { | |
| 144 if (previousBoxSizesMap().get(&m_box).contentBoxRect != m_box.contentBox Rect()) | |
| 145 return PaintInvalidationContentBoxChange; | |
| 146 } | |
| 147 | |
| 148 if (!style.hasBackground() && !style.hasBoxDecorations()) { | |
| 149 // We could let incremental invalidation cover non-composited scrollbars , but just | |
| 150 // do a full invalidation because incremental invalidation will go away with slimming paint. | |
| 151 if (reason == PaintInvalidationIncremental && m_box.hasNonCompositedScro llbars()) | |
| 152 return PaintInvalidationBorderBoxChange; | |
| 153 return reason; | |
| 154 } | |
| 155 | |
| 156 if (style.backgroundLayers().thisOrNextLayersHaveLocalAttachment()) { | |
| 157 if (previousBoxSizesMap().get(&m_box).layoutOverflowRect != m_box.layout OverflowRect()) | |
| 158 return PaintInvalidationLayoutOverflowBoxChange; | |
| 159 } | |
| 160 | |
| 161 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(m_context.oldBoun ds.size()); | |
| 162 LayoutSize newBorderBoxSize = m_box.size(); | |
| 163 | |
| 164 if (oldBorderBoxSize == newBorderBoxSize) | |
| 165 return reason; | |
| 166 | |
| 167 // LayoutBox::incrementallyInvalidatePaint() depends on positionFromPaintInv alidationBacking | |
| 168 // which is not available when slimmingPaintOffsetCachingEnabled. | |
| 169 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && (style.has BoxDecorations() || style.hasBackground())) | |
| 170 return PaintInvalidationBorderBoxChange; | |
| 171 | |
| 172 // See another hasNonCompositedScrollbars() callsite above. | |
| 173 if (m_box.hasNonCompositedScrollbars()) | |
| 174 return PaintInvalidationBorderBoxChange; | |
| 175 | |
| 176 if (style.hasVisualOverflowingEffect() || style.hasAppearance() || style.has FilterInducingProperty() || style.resize() != RESIZE_NONE) | |
| 177 return PaintInvalidationBorderBoxChange; | |
| 178 | |
| 179 if (style.hasBorderRadius()) { | |
| 180 // If a border-radius exists and width/height is smaller than radius wid th/height, | |
| 181 // we need to fully invalidate to cover the changed radius. | |
| 182 FloatRoundedRect oldRoundedRect = style.getRoundedBorderFor(LayoutRect(L ayoutPoint(0, 0), oldBorderBoxSize)); | |
| 183 FloatRoundedRect newRoundedRect = style.getRoundedBorderFor(LayoutRect(L ayoutPoint(0, 0), newBorderBoxSize)); | |
| 184 if (oldRoundedRect.getRadii() != newRoundedRect.getRadii()) | |
| 185 return PaintInvalidationBorderBoxChange; | |
| 186 } | |
| 187 | |
| 188 if (oldBorderBoxSize.width() != newBorderBoxSize.width() && m_box.mustInvali dateBackgroundOrBorderPaintOnWidthChange()) | |
| 189 return PaintInvalidationBorderBoxChange; | |
| 190 if (oldBorderBoxSize.height() != newBorderBoxSize.height() && m_box.mustInva lidateBackgroundOrBorderPaintOnHeightChange()) | |
| 191 return PaintInvalidationBorderBoxChange; | |
| 192 | |
| 193 if (reason == PaintInvalidationNone && (style.hasBackground() || style.hasBo xDecorations())) | |
|
chrishtr
2016/08/09 23:47:31
This is a little different than the old code, beca
Xianzhu
2016/08/10 16:25:01
This is caused by my careless merging of changes t
| |
| 194 reason = PaintInvalidationIncremental; | |
| 195 | |
| 196 return reason; | |
| 197 } | |
| 198 | |
| 199 PaintInvalidationReason BoxPaintInvalidator::invalidatePaintIfNeeded() | |
| 200 { | |
| 201 PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).in validatePaintIfNeededWithComputedReason(computePaintInvalidationReason()); | |
| 202 | |
|
chrishtr
2016/08/09 23:47:31
Delayed paint invalidation used to early-out here
Xianzhu
2016/08/10 16:25:01
The old code also delays other paint invalidations
| |
| 203 // For incremental invalidation, ObjectPaintInvalidator::incrementallyInvali datePaint() is | |
| 204 // not enough for LayoutBox in some cases, e.g. having border, border-radius , etc. | |
| 205 // The following will do additional incremental invalidation for LayoutBox i f needed. | |
| 206 if (reason == PaintInvalidationIncremental) | |
| 207 incrementallyInvalidatePaint(); | |
| 208 | |
| 209 if (PaintLayerScrollableArea* area = m_box.getScrollableArea()) | |
| 210 area->invalidatePaintOfScrollControlsIfNeeded(m_context); | |
| 211 | |
| 212 // This is for the next invalidatePaintIfNeeded so must be at the end. | |
| 213 savePreviousBoxSizesIfNeeded(); | |
| 214 | |
| 215 return reason; | |
| 216 } | |
| 217 | |
| 218 bool BoxPaintInvalidator::needsToSavePreviousBoxSizes() | |
| 219 { | |
| 220 LayoutSize paintInvalidationSize = m_context.newBounds.size(); | |
| 221 // Don't save old box sizes if the paint rect is empty because we'll | |
| 222 // full invalidate once the paint rect becomes non-empty. | |
| 223 if (paintInvalidationSize.isEmpty()) | |
| 224 return false; | |
| 225 | |
| 226 const ComputedStyle& style = m_box.styleRef(); | |
| 227 | |
| 228 // If we use border-box sizing we need to track changes in the size of the c ontent box. | |
| 229 if (style.boxSizing() == BoxSizingBorderBox) | |
| 230 return true; | |
| 231 | |
| 232 // We need the old box sizes only when the box has background, decorations, or masks. | |
| 233 // Main LayoutView paints base background, thus interested in box size. | |
| 234 if (!m_box.isLayoutView() && !style.hasBackground() && !style.hasBoxDecorati ons() && !style.hasMask()) | |
| 235 return false; | |
| 236 | |
| 237 // No need to save old border box size if we can use size of the old paint | |
| 238 // rect as the old border box size in the next invalidation. | |
| 239 if (paintInvalidationSize != m_box.size()) | |
| 240 return true; | |
| 241 | |
| 242 // Background and mask layers can depend on other boxes than border box. See crbug.com/490533 | |
| 243 if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.backgr oundLayers().thisOrNextLayersHaveLocalAttachment() | |
| 244 || style.maskLayers().thisOrNextLayersUseContentBox()) | |
| 245 return true; | |
| 246 | |
| 247 return false; | |
| 248 } | |
| 249 | |
| 250 void BoxPaintInvalidator::savePreviousBoxSizesIfNeeded() | |
| 251 { | |
| 252 if (!needsToSavePreviousBoxSizes()) { | |
| 253 previousBoxSizesMap().remove(&m_box); | |
| 254 return; | |
| 255 } | |
| 256 | |
| 257 PreviousBoxSizes sizes = { | |
| 258 m_box.size(), | |
| 259 m_box.contentBoxRect(), | |
| 260 m_box.layoutOverflowRect() | |
| 261 }; | |
| 262 previousBoxSizesMap().set(&m_box, sizes); | |
| 263 } | |
| 264 | |
| 265 LayoutSize BoxPaintInvalidator::computePreviousBorderBoxSize(const LayoutSize& p reviousBoundsSize) | |
| 266 { | |
| 267 // PreviousBorderBoxSize is only valid when there is background or box decor ations. | |
| 268 DCHECK(m_box.styleRef().hasBackground() || m_box.styleRef().hasBoxDecoration s()); | |
| 269 | |
| 270 auto it = previousBoxSizesMap().find(&m_box); | |
| 271 if (it != previousBoxSizesMap().end()) | |
| 272 return it->value.borderBoxSize; | |
| 273 | |
| 274 // We didn't save the old border box size because it was the same as the siz e of oldBounds. | |
| 275 return previousBoundsSize; | |
| 276 } | |
| 277 | |
| 278 } // namespace blink | |
| OLD | NEW |