blob: 2caa8892310c49d0b9ded57a97cea27b29b7b124 [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 THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIND_PROPERTIES_NEEDING_UPDATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIND_PROPERTIES_NEEDING_UPDATE_H_
#if DCHECK_IS_ON()
#include <memory>
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
namespace blink {
// This file contains a scope class 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 LayoutObject::SetNeedsPaintPropertyUpdate() or by forcing a subtree
// update (see: PaintPropertyTreeBuilderContext::force_subtree_update).
//
// This scope class works 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_OBJECT_PROPERTY_EQ(object, original, updated) \
CHECK_PROPERTY_EQ("the layout object (" << object.DebugName() << ")", \
original, updated)
class FindObjectPropertiesNeedingUpdateScope {
public:
FindObjectPropertiesNeedingUpdateScope(const LayoutObject& object,
const FragmentData& fragment_data,
bool force_subtree_update)
: object_(object),
fragment_data_(fragment_data),
needed_paint_property_update_(object.NeedsPaintPropertyUpdate()),
needed_forced_subtree_update_(force_subtree_update),
original_paint_offset_(fragment_data.PaintOffset()) {
// No need to check if an update was already needed.
if (needed_paint_property_update_ || needed_forced_subtree_update_)
return;
// Mark the properties as needing an update to ensure they are rebuilt.
object.GetMutableForPainting()
.SetOnlyThisNeedsPaintPropertyUpdateForTesting();
if (const auto* properties = fragment_data_.PaintProperties())
original_properties_ = properties->Clone();
if (fragment_data_.HasLocalBorderBoxProperties()) {
original_local_border_box_properties_ =
std::make_unique<PropertyTreeState>(
fragment_data_.LocalBorderBoxProperties());
}
}
~FindObjectPropertiesNeedingUpdateScope() {
// Paint offset and paintOffsetTranslation should not change under
// FindObjectPropertiesNeedingUpdateScope no matter if we needed paint
// property update.
LayoutPoint paint_offset = fragment_data_.PaintOffset();
DCHECK_OBJECT_PROPERTY_EQ(object_, &original_paint_offset_, &paint_offset);
const auto* object_properties = fragment_data_.PaintProperties();
if (original_properties_ && object_properties) {
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_properties_->PaintOffsetTranslation(),
object_properties->PaintOffsetTranslation());
}
// No need to check if an update was already needed.
if (needed_paint_property_update_ || needed_forced_subtree_update_)
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::force_subtree_update).
if (original_properties_ && object_properties) {
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Transform(),
object_properties->Transform());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Effect(),
object_properties->Effect());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Filter(),
object_properties->Filter());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Mask(),
object_properties->Mask());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->ClipPath(),
object_properties->ClipPath());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->ClipPathClip(),
object_properties->ClipPathClip());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->MaskClip(),
object_properties->MaskClip());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->CssClip(),
object_properties->CssClip());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_properties_->CssClipFixedPosition(),
object_properties->CssClipFixedPosition());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_properties_->OverflowControlsClip(),
object_properties->OverflowControlsClip());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_properties_->InnerBorderRadiusClip(),
object_properties->InnerBorderRadiusClip());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->OverflowClip(),
object_properties->OverflowClip());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Perspective(),
object_properties->Perspective());
DCHECK_OBJECT_PROPERTY_EQ(
object_, original_properties_->SvgLocalToBorderBoxTransform(),
object_properties->SvgLocalToBorderBoxTransform());
DCHECK_OBJECT_PROPERTY_EQ(object_, original_properties_->Scroll(),
object_properties->Scroll());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_properties_->ScrollTranslation(),
object_properties->ScrollTranslation());
} else {
DCHECK_EQ(!!original_properties_, !!object_properties)
<< " Object: " << object_.DebugName();
}
if (original_local_border_box_properties_ &&
fragment_data_.HasLocalBorderBoxProperties()) {
const auto object_border_box = fragment_data_.LocalBorderBoxProperties();
DCHECK_OBJECT_PROPERTY_EQ(
object_, original_local_border_box_properties_->Transform(),
object_border_box.Transform());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_local_border_box_properties_->Clip(),
object_border_box.Clip());
DCHECK_OBJECT_PROPERTY_EQ(object_,
original_local_border_box_properties_->Effect(),
object_border_box.Effect());
} else {
DCHECK_EQ(!!original_local_border_box_properties_,
fragment_data_.HasLocalBorderBoxProperties())
<< " Object: " << object_.DebugName();
}
// Restore original clean bit.
object_.GetMutableForPainting().ClearNeedsPaintPropertyUpdateForTesting();
}
private:
const LayoutObject& object_;
const FragmentData& fragment_data_;
bool needed_paint_property_update_;
bool needed_forced_subtree_update_;
LayoutPoint original_paint_offset_;
std::unique_ptr<const ObjectPaintProperties> original_properties_;
std::unique_ptr<const PropertyTreeState>
original_local_border_box_properties_;
};
} // namespace blink
#endif // DCHECK_IS_ON()
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIND_PROPERTIES_NEEDING_UPDATE_H_