blob: b9f89bcbe3d8a2ca1a6959d00148d2765833742d [file] [log] [blame]
// 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.
#ifndef FindPropertiesNeedingUpdate_h
#define FindPropertiesNeedingUpdate_h
#if DCHECK_IS_ON()
namespace blink {
// This file contains two scope classes for catching cases where paint
// properties needed an update but were not marked as such. If paint properties
// will change, the object must be marked as needing a paint property update
// using {FrameView, LayoutObject}::setNeedsPaintPropertyUpdate() or by forcing
// a subtree update (see: PaintPropertyTreeBuilderContext::forceSubtreeUpdate).
//
// Both scope classes work by recording the paint property state of an object
// before rebuilding properties, forcing the properties to get updated, then
// checking that the updated properties match the original properties.
#define DUMP_PROPERTIES(original, updated) \
"\nOriginal:\n" \
<< (original ? (original)->toString().ascii().data() : "null") \
<< "\nUpdated:\n" \
<< (updated ? (updated)->toString().ascii().data() : "null")
#define CHECK_PROPERTY_EQ(thing, original, updated) \
do { \
DCHECK(!!original == !!updated) << "Property was created or deleted " \
<< "without " << thing \
<< " needing a paint property update." \
<< DUMP_PROPERTIES(original, updated); \
if (!!original && !!updated) { \
DCHECK(*original == *updated) << "Property was updated without " \
<< thing \
<< " needing a paint property update." \
<< DUMP_PROPERTIES(original, updated); \
} \
} while (0)
#define DCHECK_FRAMEVIEW_PROPERTY_EQ(original, updated) \
CHECK_PROPERTY_EQ("the FrameView", original, updated)
class FindFrameViewPropertiesNeedingUpdateScope {
public:
FindFrameViewPropertiesNeedingUpdateScope(
FrameView* frameView,
PaintPropertyTreeBuilderContext& context)
: m_frameView(frameView),
m_neededPaintPropertyUpdate(frameView->needsPaintPropertyUpdate()),
m_neededForcedSubtreeUpdate(context.forceSubtreeUpdate) {
// No need to check if an update was already needed.
if (m_neededPaintPropertyUpdate || m_neededForcedSubtreeUpdate)
return;
// Mark the properties as needing an update to ensure they are rebuilt.
m_frameView->setOnlyThisNeedsPaintPropertyUpdateForTesting();
if (auto* preTranslation = m_frameView->preTranslation())
m_originalPreTranslation = preTranslation->clone();
if (auto* contentClip = m_frameView->contentClip())
m_originalContentClip = contentClip->clone();
if (auto* scrollTranslation = m_frameView->scrollTranslation())
m_originalScrollTranslation = scrollTranslation->clone();
}
~FindFrameViewPropertiesNeedingUpdateScope() {
// No need to check if an update was already needed.
if (m_neededPaintPropertyUpdate || m_neededForcedSubtreeUpdate)
return;
// If these checks fail, the paint properties changed unexpectedly. This is
// due to missing one of these paint property invalidations:
// 1) The FrameView should have been marked as needing an update with
// FrameView::setNeedsPaintPropertyUpdate().
// 2) The PrePaintTreeWalk should have had a forced subtree update (see:
// PaintPropertyTreeBuilderContext::forceSubtreeUpdate).
DCHECK_FRAMEVIEW_PROPERTY_EQ(m_originalPreTranslation,
m_frameView->preTranslation());
DCHECK_FRAMEVIEW_PROPERTY_EQ(m_originalContentClip,
m_frameView->contentClip());
DCHECK_FRAMEVIEW_PROPERTY_EQ(m_originalScrollTranslation,
m_frameView->scrollTranslation());
// Restore original clean bit.
m_frameView->clearNeedsPaintPropertyUpdate();
}
private:
Persistent<FrameView> m_frameView;
bool m_neededPaintPropertyUpdate;
bool m_neededForcedSubtreeUpdate;
RefPtr<TransformPaintPropertyNode> m_originalPreTranslation;
RefPtr<ClipPaintPropertyNode> m_originalContentClip;
RefPtr<TransformPaintPropertyNode> m_originalScrollTranslation;
};
#define DCHECK_OBJECT_PROPERTY_EQ(object, original, updated) \
CHECK_PROPERTY_EQ("the layout object (" << object.debugName() << ")", \
original, updated)
class FindObjectPropertiesNeedingUpdateScope {
public:
FindObjectPropertiesNeedingUpdateScope(
const LayoutObject& object,
PaintPropertyTreeBuilderContext& context)
: m_object(object),
m_neededPaintPropertyUpdate(object.needsPaintPropertyUpdate()),
m_neededForcedSubtreeUpdate(context.forceSubtreeUpdate),
m_originalPaintOffset(object.paintOffset()) {
// No need to check if an update was already needed.
if (m_neededPaintPropertyUpdate || m_neededForcedSubtreeUpdate)
return;
// Mark the properties as needing an update to ensure they are rebuilt.
m_object.getMutableForPainting()
.setOnlyThisNeedsPaintPropertyUpdateForTesting();
if (const auto* properties = m_object.paintProperties())
m_originalProperties = properties->clone();
}
~FindObjectPropertiesNeedingUpdateScope() {
// No need to check if an update was already needed.
if (m_neededPaintPropertyUpdate || m_neededForcedSubtreeUpdate)
return;
// If these checks fail, the paint properties changed unexpectedly. This is
// due to missing one of these paint property invalidations:
// 1) The LayoutObject should have been marked as needing an update with
// LayoutObject::setNeedsPaintPropertyUpdate().
// 2) The PrePaintTreeWalk should have had a forced subtree update (see:
// PaintPropertyTreeBuilderContext::forceSubtreeUpdate).
DCHECK_OBJECT_PROPERTY_EQ(m_object, &m_originalPaintOffset,
&m_object.paintOffset());
const auto* objectProperties = m_object.paintProperties();
if (m_originalProperties && objectProperties) {
DCHECK_OBJECT_PROPERTY_EQ(m_object,
m_originalProperties->paintOffsetTranslation(),
objectProperties->paintOffsetTranslation());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->transform(),
objectProperties->transform());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->effect(),
objectProperties->effect());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->filter(),
objectProperties->filter());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->mask(),
objectProperties->mask());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->maskClip(),
objectProperties->maskClip());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->cssClip(),
objectProperties->cssClip());
DCHECK_OBJECT_PROPERTY_EQ(m_object,
m_originalProperties->cssClipFixedPosition(),
objectProperties->cssClipFixedPosition());
DCHECK_OBJECT_PROPERTY_EQ(m_object,
m_originalProperties->innerBorderRadiusClip(),
objectProperties->innerBorderRadiusClip());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->overflowClip(),
objectProperties->overflowClip());
DCHECK_OBJECT_PROPERTY_EQ(m_object, m_originalProperties->perspective(),
objectProperties->perspective());
DCHECK_OBJECT_PROPERTY_EQ(
m_object, m_originalProperties->svgLocalToBorderBoxTransform(),
objectProperties->svgLocalToBorderBoxTransform());
DCHECK_OBJECT_PROPERTY_EQ(m_object,
m_originalProperties->scrollTranslation(),
objectProperties->scrollTranslation());
DCHECK_OBJECT_PROPERTY_EQ(m_object,
m_originalProperties->scrollbarPaintOffset(),
objectProperties->scrollbarPaintOffset());
const auto* originalBorderBox =
m_originalProperties->localBorderBoxProperties();
const auto* objectBorderBox =
objectProperties->localBorderBoxProperties();
if (originalBorderBox && objectBorderBox) {
DCHECK_OBJECT_PROPERTY_EQ(m_object, originalBorderBox->transform(),
objectBorderBox->transform());
DCHECK_OBJECT_PROPERTY_EQ(m_object, originalBorderBox->clip(),
objectBorderBox->clip());
DCHECK_OBJECT_PROPERTY_EQ(m_object, originalBorderBox->effect(),
objectBorderBox->effect());
} else {
DCHECK_EQ(!!originalBorderBox, !!objectBorderBox)
<< " Object: " << m_object.debugName();
}
} else {
DCHECK_EQ(!!m_originalProperties, !!objectProperties)
<< " Object: " << m_object.debugName();
}
// Restore original clean bit.
m_object.getMutableForPainting().clearNeedsPaintPropertyUpdateForTesting();
}
private:
const LayoutObject& m_object;
bool m_neededPaintPropertyUpdate;
bool m_neededForcedSubtreeUpdate;
LayoutPoint m_originalPaintOffset;
std::unique_ptr<const ObjectPaintProperties> m_originalProperties;
};
} // namespace blink
#endif // DCHECK_IS_ON()
#endif // FindPropertiesNeedingUpdate_h