| // 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() |
| |
| #include "core/frame/LocalFrameView.h" |
| #include "core/layout/LayoutObject.h" |
| #include "core/paint/ObjectPaintProperties.h" |
| #include "core/paint/PaintPropertyTreeBuilder.h" |
| |
| 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 {LocalFrameView, LayoutObject}::SetNeedsPaintPropertyUpdate() or by |
| // forcing a subtree update (see: |
| // PaintPropertyTreeBuilderContext::force_subtree_update). |
| // |
| // 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 LocalFrameView", original, updated) |
| |
| class FindFrameViewPropertiesNeedingUpdateScope { |
| public: |
| FindFrameViewPropertiesNeedingUpdateScope(LocalFrameView* frame_view, |
| bool force_subtree_update) |
| : frame_view_(frame_view), |
| needed_paint_property_update_(frame_view->NeedsPaintPropertyUpdate()), |
| needed_forced_subtree_update_(force_subtree_update) { |
| // 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. |
| frame_view_->SetOnlyThisNeedsPaintPropertyUpdateForTesting(); |
| |
| if (auto* pre_translation = frame_view_->PreTranslation()) |
| original_pre_translation_ = pre_translation->Clone(); |
| if (auto* content_clip = frame_view_->ContentClip()) |
| original_content_clip_ = content_clip->Clone(); |
| if (auto* scroll_node = frame_view_->ScrollNode()) |
| original_scroll_node_ = scroll_node->Clone(); |
| if (auto* scroll_translation = frame_view_->ScrollTranslation()) |
| original_scroll_translation_ = scroll_translation->Clone(); |
| } |
| |
| ~FindFrameViewPropertiesNeedingUpdateScope() { |
| // 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 LocalFrameView should have been marked as needing an update with |
| // LocalFrameView::SetNeedsPaintPropertyUpdate(). |
| // 2) The PrePaintTreeWalk should have had a forced subtree update (see: |
| // PaintPropertyTreeBuilderContext::force_subtree_update). |
| DCHECK_FRAMEVIEW_PROPERTY_EQ(original_pre_translation_, |
| frame_view_->PreTranslation()); |
| DCHECK_FRAMEVIEW_PROPERTY_EQ(original_content_clip_, |
| frame_view_->ContentClip()); |
| DCHECK_FRAMEVIEW_PROPERTY_EQ(original_scroll_node_, |
| frame_view_->ScrollNode()); |
| DCHECK_FRAMEVIEW_PROPERTY_EQ(original_scroll_translation_, |
| frame_view_->ScrollTranslation()); |
| |
| // Restore original clean bit. |
| frame_view_->ClearNeedsPaintPropertyUpdate(); |
| } |
| |
| private: |
| Persistent<LocalFrameView> frame_view_; |
| bool needed_paint_property_update_; |
| bool needed_forced_subtree_update_; |
| scoped_refptr<const TransformPaintPropertyNode> original_pre_translation_; |
| scoped_refptr<const ClipPaintPropertyNode> original_content_clip_; |
| scoped_refptr<const ScrollPaintPropertyNode> original_scroll_node_; |
| scoped_refptr<const TransformPaintPropertyNode> original_scroll_translation_; |
| }; |
| |
| #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 (const auto* local_border_box = |
| fragment_data_.LocalBorderBoxProperties()) { |
| original_local_border_box_properties_ = |
| WTF::WrapUnique(new PropertyTreeState(*local_border_box)); |
| } |
| } |
| |
| ~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_->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(); |
| } |
| |
| const auto* object_border_box = fragment_data_.LocalBorderBoxProperties(); |
| if (original_local_border_box_properties_ && object_border_box) { |
| 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_, !!object_border_box) |
| << " 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 // FindPropertiesNeedingUpdate_h |