blob: d4cf09c57e016efe5795306f758d900fbf360b91 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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 <optional>
#include "base/dcheck_is_on.h"
#include "base/gtest_prod_util.h"
#include "third_party/blink/renderer/core/core_export.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/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
class LayoutObject;
class LocalFrameView;
class PhysicalBoxFragment;
class PhysicalFragment;
struct PhysicalFragmentLink;
// 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 final {
STACK_ALLOCATED();
public:
PrePaintTreeWalk() = default;
void WalkTree(LocalFrameView& root_frame);
static bool ObjectRequiresPrePaint(const LayoutObject&);
static bool ObjectRequiresTreeBuilderContext(const LayoutObject&);
// Keeps information about the parent fragment that we need to search inside
// to find out-of-flow positioned descendants, and also which fragmentainer
// we're inside (which will serve as a fragment ID in FragmentData).
struct ContainingFragment {
STACK_ALLOCATED();
public:
bool IsInFragmentationContext() const;
const PhysicalBoxFragment* fragment = nullptr;
wtf_size_t fragmentainer_idx = WTF::kNotFound;
int fragmentation_nesting_level = 0;
};
// This provides a default base copy constructor for PrePaintTreeWalkContext.
// It contains all fields except for tree_builder_context which needs special
// treatment in the copy constructor.
struct PrePaintTreeWalkContextBase {
STACK_ALLOCATED();
protected:
PrePaintTreeWalkContextBase() = default;
PrePaintTreeWalkContextBase(const PrePaintTreeWalkContextBase&) = default;
public:
// Reset fragmentation when entering something that shouldn't be affected by
// the current fragmentation context(s).
void ResetFragmentation() {
current_container = {};
absolute_positioned_container = {};
fixed_positioned_container = {};
}
PaintInvalidatorContext paint_invalidator_context;
// 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;
// True if we're visiting the parent for the first time, i.e. when we're in
// the first fragmentainer where the parent occurs (or if we're not
// fragmented at all).
bool is_parent_first_for_node = true;
ContainingFragment current_container;
ContainingFragment absolute_positioned_container;
ContainingFragment fixed_positioned_container;
};
struct PrePaintTreeWalkContext : public PrePaintTreeWalkContextBase {
PrePaintTreeWalkContext() { tree_builder_context.emplace(); }
PrePaintTreeWalkContext(const PrePaintTreeWalkContext& parent_context,
bool needs_tree_builder_context)
: PrePaintTreeWalkContextBase(parent_context) {
if (needs_tree_builder_context
#if DCHECK_IS_ON()
|| RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()
#endif
) {
DCHECK(parent_context.tree_builder_context);
tree_builder_context.emplace(*parent_context.tree_builder_context);
#if DCHECK_IS_ON()
DCHECK(!needs_tree_builder_context ||
parent_context.tree_builder_context->is_actually_needed);
tree_builder_context->is_actually_needed = needs_tree_builder_context;
#endif
}
}
PrePaintTreeWalkContext(const PrePaintTreeWalkContext&) = delete;
PrePaintTreeWalkContext& operator=(const PrePaintTreeWalkContext&) = delete;
bool NeedsTreeBuilderContext() const {
return tree_builder_context.has_value()
#if DCHECK_IS_ON()
&& tree_builder_context->is_actually_needed
#endif
;
}
std::optional<PaintPropertyTreeBuilderContext> tree_builder_context;
};
static bool ContextRequiresChildPrePaint(const PrePaintTreeWalkContext&);
static bool ContextRequiresChildTreeBuilderContext(
const PrePaintTreeWalkContext&);
#if DCHECK_IS_ON()
void CheckTreeBuilderContextState(const LayoutObject&,
const PrePaintTreeWalkContext&);
#endif
// Upon entering a child LayoutObject, create an PrePaintInfo, and populate
// everything except its FragmentData. We need to get a bit further inside the
// child (WalkInternal()) before we can set up FragmentData (if we get there
// at all).
PrePaintInfo CreatePrePaintInfo(const PhysicalFragmentLink& child,
const PrePaintTreeWalkContext& context);
// Locate and/or set up a FragmentData object for the current object /
// physical fragment.
FragmentData* GetOrCreateFragmentData(const LayoutObject&,
const PrePaintTreeWalkContext&,
const PrePaintInfo&);
void UpdateContextForOOFContainer(const LayoutObject&,
PrePaintTreeWalkContext&,
const PhysicalBoxFragment*);
void Walk(LocalFrameView&, const PrePaintTreeWalkContext& parent_context);
// 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&,
PrePaintTreeWalkContext&,
PrePaintInfo*);
// Add any "missable" children to a list. Missable children are children that
// we might not find during LayoutObject traversal. This happens when an
// ancestor LayoutObject (of the missable child) has no fragment inside a
// given fragmentainer, e.g. when there's an OOF fragment, but its containing
// block has no fragment inside that fragmentainer. Later, during the child
// walk, when a missable child is actually walked, it's removed from the
// list.
//
// Returns true if there are any missable children inside the fragment, false
// otherwise.
bool CollectMissableChildren(PrePaintTreeWalkContext&,
const PhysicalBoxFragment&);
// Based on the context established by |ancestor|, modify it to become as
// correct as possible for |object|. Any object between the ancestor and the
// target object may have paint effects that would be missed otherwise.
//
// This function will start by walking up to the ancestor recursively, and
// then build whatever it can on the way down again. If a physical fragment is
// returned, this will be the parent fragment of the next child, so that we
// can search for a fragment for the child right there. If the child is
// out-of-flow positioned, it will need to locate the correct containing
// fragment via other means, though. If it's nullptr, it means that no
// fragment exists for the parent (i.e. the node isn't represented in this
// fragmentainer), and we need to behave according to specs (assume that a
// transform origin is based on a zero-block-size box, zero clip rectangle
// size, etc.)
const PhysicalBoxFragment* RebuildContextForMissedDescendant(
const PhysicalBoxFragment& ancestor,
const LayoutObject& object,
bool update_tree_builder_context,
PrePaintTreeWalkContext&);
// Walk any missed children (i.e. those collected by CollectMissableChildren()
// and not walked by Walk()) after child object traversal.
void WalkMissedChildren(const PhysicalBoxFragment&,
bool is_in_fragment_traversal,
const PrePaintTreeWalkContext&);
void WalkFragmentationContextRootChildren(const LayoutObject&,
const PhysicalBoxFragment&,
const PrePaintTreeWalkContext&);
void WalkFragmentainer(const LayoutObject& parent_object,
const PhysicalFragmentLink& child_link,
const PrePaintTreeWalkContext& parent_context,
wtf_size_t inner_fragmentainer_idx = 0);
void WalkLayoutObjectChildren(const LayoutObject&,
const PhysicalBoxFragment*,
const PrePaintTreeWalkContext&);
void WalkChildren(const LayoutObject&,
const PhysicalBoxFragment*,
PrePaintTreeWalkContext&,
bool is_inside_fragment_child = false);
void Walk(const LayoutObject&,
const PrePaintTreeWalkContext& parent_context,
PrePaintInfo*);
bool NeedsTreeBuilderContextUpdate(const LocalFrameView&,
const PrePaintTreeWalkContext&);
bool NeedsTreeBuilderContextUpdate(const LayoutObject&,
const 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&);
PaintInvalidator paint_invalidator_;
// List of fragments that may be missed during LayoutObject walking. See
// CollectMissableChildren() and WalkMissedChildren().
HeapHashSet<Member<const PhysicalFragment>> pending_missables_;
bool needs_invalidate_chrome_client_and_intersection_ = false;
FRIEND_TEST_ALL_PREFIXES(PrePaintTreeWalkTest, ClipRects);
};
} // namespace blink
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
blink::PrePaintTreeWalk::PrePaintTreeWalkContext)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PRE_PAINT_TREE_WALK_H_