blob: 3e4ddc8f5c2f13457370868dec713f7f6126039c [file] [log] [blame]
// Copyright 2015 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_PRE_PAINT_TREE_WALK_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PRE_PAINT_TREE_WALK_H_
#include "third_party/blink/renderer/core/paint/clip_rect.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
class LayoutObject;
class LocalFrameView;
class NGFragmentChildIterator;
// This class walks the whole layout tree, beginning from the root
// LocalFrameView, across frame boundaries. Helper classes are called for each
// tree node to perform actual actions. It expects to be invoked in InPrePaint
// phase.
class CORE_EXPORT PrePaintTreeWalk {
DISALLOW_NEW();
public:
PrePaintTreeWalk() = default;
void WalkTree(LocalFrameView& root_frame);
static bool ObjectRequiresPrePaint(const LayoutObject&);
static bool ObjectRequiresTreeBuilderContext(const LayoutObject&);
private:
friend PaintInvalidatorContext::ParentContextAccessor;
// PrePaintTreewalkContext is large and can lead to stack overflows
// when recursion is deep so these context objects are allocated on the heap.
// See: https://crbug.com/698653.
struct PrePaintTreeWalkContext {
PrePaintTreeWalkContext() { tree_builder_context.emplace(); }
PrePaintTreeWalkContext(
const PrePaintTreeWalkContext& parent_context,
const PaintInvalidatorContext::ParentContextAccessor&
parent_context_accessor,
bool needs_tree_builder_context)
: paint_invalidator_context(parent_context_accessor),
ancestor_scroll_container_paint_layer(
parent_context.ancestor_scroll_container_paint_layer),
inside_blocking_touch_event_handler(
parent_context.inside_blocking_touch_event_handler),
effective_allowed_touch_action_changed(
parent_context.effective_allowed_touch_action_changed),
inside_blocking_wheel_event_handler(
parent_context.inside_blocking_wheel_event_handler),
blocking_wheel_event_handler_changed(
parent_context.blocking_wheel_event_handler_changed),
clip_changed(parent_context.clip_changed),
paint_invalidation_container(
parent_context.paint_invalidation_container),
paint_invalidation_container_for_stacked_contents(
parent_context
.paint_invalidation_container_for_stacked_contents) {
if (needs_tree_builder_context || DCHECK_IS_ON()) {
DCHECK(parent_context.tree_builder_context);
tree_builder_context.emplace(*parent_context.tree_builder_context);
}
#if DCHECK_IS_ON()
if (needs_tree_builder_context)
DCHECK(parent_context.tree_builder_context->is_actually_needed);
tree_builder_context->is_actually_needed = needs_tree_builder_context;
#endif
}
base::Optional<PaintPropertyTreeBuilderContext> tree_builder_context;
PaintInvalidatorContext paint_invalidator_context;
bool NeedsTreeBuilderContext() const {
#if DCHECK_IS_ON()
DCHECK(tree_builder_context);
return tree_builder_context->is_actually_needed;
#else
return tree_builder_context.has_value();
#endif
}
// The ancestor in the PaintLayer tree which is a scroll container. Note
// that it is tree ancestor, not containing block or stacking ancestor.
PaintLayer* ancestor_scroll_container_paint_layer = nullptr;
// Whether there is a blocking touch event handler on any ancestor.
bool inside_blocking_touch_event_handler = false;
// When the effective allowed touch action changes on an ancestor, the
// entire subtree may need to update.
bool effective_allowed_touch_action_changed = false;
// Whether there is a blocking wheel event handler on any ancestor.
bool inside_blocking_wheel_event_handler = false;
// When the blocking wheel event handlers change on an ancestor, the entire
// subtree may need to update.
bool blocking_wheel_event_handler_changed = false;
// This is set to true once we see tree_builder_context->clip_changed is
// true. It will be propagated to descendant contexts even if we don't
// create tree_builder_context. Used only when CullRectUpdate is not
// enabled.
bool clip_changed = false;
const LayoutBoxModelObject* paint_invalidation_container = nullptr;
const LayoutBoxModelObject*
paint_invalidation_container_for_stacked_contents = nullptr;
};
static bool ContextRequiresChildPrePaint(const PrePaintTreeWalkContext&);
static bool ContextRequiresChildTreeBuilderContext(
const PrePaintTreeWalkContext&);
#if DCHECK_IS_ON()
void CheckTreeBuilderContextState(const LayoutObject&,
const PrePaintTreeWalkContext&);
#endif
const PrePaintTreeWalkContext& ContextAt(wtf_size_t index) {
DCHECK_LT(index, context_storage_.size());
return context_storage_[index];
}
void Walk(LocalFrameView&);
// This is to minimize stack frame usage during recursion. Modern compilers
// (MSVC in particular) can inline across compilation units, resulting in
// very big stack frames. Splitting the heavy lifting to a separate function
// makes sure the stack frame is freed prior to making a recursive call.
// See https://crbug.com/781301 .
NOINLINE void WalkInternal(const LayoutObject&,
const NGFragmentChildIterator*,
PrePaintTreeWalkContext&);
void WalkNGChildren(const LayoutObject* parent, NGFragmentChildIterator*);
void WalkLegacyChildren(const LayoutObject&);
void WalkChildren(const LayoutObject*, const NGFragmentChildIterator*);
void Walk(const LayoutObject&, const NGFragmentChildIterator*);
bool NeedsTreeBuilderContextUpdate(const LocalFrameView&,
const PrePaintTreeWalkContext&);
void UpdateAuxiliaryObjectProperties(const LayoutObject&,
PrePaintTreeWalkContext&);
// Updates |LayoutObject::InsideBlockingTouchEventHandler|. Also ensures
// |PrePaintTreeWalkContext.effective_allowed_touch_action_changed| is set
// which will ensure the subtree is updated too.
void UpdateEffectiveAllowedTouchAction(const LayoutObject&,
PrePaintTreeWalkContext&);
// Updates |LayoutObject::InsideBlockingWheelEventHandler|. Also ensures
// |PrePaintTreeWalkContext.blocking_wheel_event_handler_changed| is set
// which will ensure the subtree is updated too.
void UpdateBlockingWheelEventHandler(const LayoutObject&,
PrePaintTreeWalkContext&);
void InvalidatePaintForHitTesting(const LayoutObject&,
PrePaintTreeWalkContext&);
void ResizeContextStorageIfNeeded();
void UpdatePaintInvalidationContainer(const LayoutObject& object,
const PaintLayer* painting_layer,
PrePaintTreeWalkContext& context,
bool is_ng_painting);
PaintInvalidator paint_invalidator_;
Vector<PrePaintTreeWalkContext> context_storage_;
// TODO(https://crbug.com/841364): Remove is_wheel_event_regions_enabled
// argument once kWheelEventRegions feature flag is removed.
bool is_wheel_event_regions_enabled_ = false;
bool needs_invalidate_chrome_client_ = false;
FRIEND_TEST_ALL_PREFIXES(PrePaintTreeWalkTest, ClipRects);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PRE_PAINT_TREE_WALK_H_