| // Copyright 2016 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_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_H_ |
| |
| #include <optional> |
| |
| #include "base/functional/callback.h" |
| #include "base/time/time.h" |
| #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h" |
| #include "third_party/blink/renderer/core/intersection_observer/intersection_observation.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" |
| #include "third_party/blink/renderer/platform/geometry/length.h" |
| #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" |
| #include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_hash_set.h" |
| #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" |
| #include "third_party/blink/renderer/platform/heap/forward.h" |
| #include "third_party/blink/renderer/platform/heap/garbage_collected.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| |
| namespace blink { |
| |
| class Document; |
| class Element; |
| class ExceptionState; |
| class IntersectionObserverDelegate; |
| class IntersectionObserverEntry; |
| class IntersectionObserverInit; |
| class Node; |
| class ScriptState; |
| class V8IntersectionObserverCallback; |
| |
| class CORE_EXPORT IntersectionObserver final |
| : public ScriptWrappable, |
| public ActiveScriptWrappable<IntersectionObserver>, |
| public ExecutionContextClient { |
| DEFINE_WRAPPERTYPEINFO(); |
| |
| public: |
| using EventCallback = base::RepeatingCallback<void( |
| const HeapVector<Member<IntersectionObserverEntry>>&)>; |
| |
| // The IntersectionObserver can be configured to notify based on changes to |
| // how much of the target element's area intersects with the root, or based on |
| // changes to how much of the root element's area intersects with the |
| // target. Examples illustrating the distinction: |
| // |
| // 1.0 of target, 0.5 of target, 1.0 of target, |
| // 0.25 of root 0.5 of root 1.0 of root |
| // +------------------+ +------------------+ *~~~~~~~~~~~~~~~~~~* |
| // | ////////// | | | ;//////////////////; |
| // | ////////// | | | ;//////////////////; |
| // | ////////// | ;//////////////////; ;//////////////////; |
| // | | ;//////////////////; ;//////////////////; |
| // +------------------+ *~~~~~~~~~~~~~~~~~~* *~~~~~~~~~~~~~~~~~~* |
| // //////////////////// |
| // //////////////////// |
| // //////////////////// |
| enum ThresholdInterpretation { kFractionOfTarget, kFractionOfRoot }; |
| |
| // This value can be used to detect transitions between non-intersecting or |
| // edge-adjacent (i.e., zero area) state, and intersecting by any non-zero |
| // number of pixels. |
| static constexpr float kMinimumThreshold = |
| IntersectionGeometry::kMinimumThreshold; |
| |
| // Used to specify when callbacks should be invoked with new notifications. |
| // Blink-internal users of IntersectionObserver will have their callbacks |
| // invoked synchronously either at the end of a lifecycle update or in the |
| // middle of the lifecycle post layout. Javascript observers will PostTask to |
| // invoke their callbacks. |
| enum DeliveryBehavior { |
| kDeliverDuringPostLayoutSteps, |
| kDeliverDuringPostLifecycleSteps, |
| kPostTaskToDeliver |
| }; |
| |
| // Used to specify whether the margins apply to the root element or the source |
| // element. The effect of the root element margins is that intermediate |
| // scrollers clip content by its bounding box without considering margins. |
| // That is, margins only apply to the last scroller (root). The effect of |
| // source element margins is that the margins apply to the first / deepest |
| // clipper, but do not apply to any other clippers. Note that in a case of a |
| // single clipper, the two approaches are equivalent. |
| // |
| // Note that the percentage margin is resolved against the root rect, even |
| // when the margin is applied to the target. |
| enum MarginTarget { kApplyMarginToRoot, kApplyMarginToTarget }; |
| |
| static IntersectionObserver* Create( |
| const IntersectionObserverInit*, |
| IntersectionObserverDelegate&, |
| std::optional<LocalFrameUkmAggregator::MetricId> ukm_metric_id, |
| ExceptionState& = ASSERT_NO_EXCEPTION); |
| static IntersectionObserver* Create(ScriptState*, |
| V8IntersectionObserverCallback*, |
| const IntersectionObserverInit*, |
| ExceptionState& = ASSERT_NO_EXCEPTION); |
| |
| struct Params { |
| STACK_ALLOCATED(); |
| |
| public: |
| Node* root; |
| Vector<Length> margin; |
| MarginTarget margin_target = kApplyMarginToRoot; |
| Vector<Length> scroll_margin; |
| |
| // Elements should be in the range [0,1], and are interpreted according to |
| // the given `semantics`. |
| Vector<float> thresholds; |
| ThresholdInterpretation semantics = kFractionOfTarget; |
| |
| DeliveryBehavior behavior = kDeliverDuringPostLifecycleSteps; |
| // Specifies the minimum period between change notifications. |
| base::TimeDelta delay; |
| bool track_visibility = false; |
| bool always_report_root_bounds = false; |
| // Indicates whether the overflow clip edge should be used instead of the |
| // bounding box if appropriate. |
| bool use_overflow_clip_edge = false; |
| |
| // Indicates whether we should compute and expose the occluder node Id. |
| // This only works if you've already set true `track_visibility`. |
| bool expose_occluder_id = false; |
| }; |
| |
| // Creates an IntersectionObserver that monitors changes to the intersection |
| // and notifies via the given |callback|. |
| static IntersectionObserver* Create( |
| const Document& document, |
| EventCallback callback, |
| std::optional<LocalFrameUkmAggregator::MetricId> ukm_metric_id, |
| Params&& params); |
| |
| IntersectionObserver( |
| IntersectionObserverDelegate& delegate, |
| std::optional<LocalFrameUkmAggregator::MetricId> ukm_metric_id, |
| Params&& params); |
| |
| // API methods. |
| void observe(Element*, ExceptionState& = ASSERT_NO_EXCEPTION); |
| void unobserve(Element*, ExceptionState& = ASSERT_NO_EXCEPTION); |
| void disconnect(ExceptionState& = ASSERT_NO_EXCEPTION); |
| HeapVector<Member<IntersectionObserverEntry>> takeRecords( |
| ExceptionState& = ASSERT_NO_EXCEPTION); |
| |
| // API attributes. |
| Node* root() const { return root_.Get(); } |
| String rootMargin() const; |
| String scrollMargin() const; |
| const Vector<float>& thresholds() const { return thresholds_; } |
| DOMHighResTimeStamp delay() const { return delay_.InMilliseconds(); } |
| bool trackVisibility() const { return track_visibility_; } |
| bool trackFractionOfRoot() const { return track_fraction_of_root_; } |
| |
| // An observer can either track intersections with an explicit root Node, |
| // or with the the top-level frame's viewport (the "implicit root"). When |
| // tracking the implicit root, root_ will be null, but because root_ is a |
| // weak pointer, we cannot surmise that this observer tracks the implicit |
| // root just because root_ is null. Hence root_is_implicit_. |
| bool RootIsImplicit() const { return root_is_implicit_; } |
| |
| bool HasObservations() const { return !observations_.empty(); } |
| bool AlwaysReportRootBounds() const { return always_report_root_bounds_; } |
| bool NeedsOcclusionTracking() const { |
| return trackVisibility() && !observations_.empty(); |
| } |
| |
| bool ShouldExposeOccluderNodeId() const { |
| return trackVisibility() && expose_occluder_id_; |
| } |
| |
| base::TimeDelta GetEffectiveDelay() const; |
| |
| Vector<Length> RootMargin() const { |
| return margin_target_ == kApplyMarginToRoot ? margin_ : Vector<Length>(); |
| } |
| |
| Vector<Length> TargetMargin() const { |
| return margin_target_ == kApplyMarginToTarget ? margin_ : Vector<Length>(); |
| } |
| |
| Vector<Length> ScrollMargin() const { return scroll_margin_; } |
| |
| bool IsInternal() const; |
| // The metric id for tracking update time via UpdateTime metrics, or null for |
| // internal intersection observers without explicit metrics. |
| std::optional<LocalFrameUkmAggregator::MetricId> GetUkmMetricId() const { |
| return ukm_metric_id_; |
| } |
| |
| void ReportUpdates(IntersectionObservation&); |
| DeliveryBehavior GetDeliveryBehavior() const; |
| void Deliver(); |
| |
| // Returns false if this observer has an explicit root node which has been |
| // deleted; true otherwise. |
| bool RootIsValid() const; |
| |
| bool UseOverflowClipEdge() const { return use_overflow_clip_edge_ == 1; } |
| |
| // ScriptWrappable override: |
| bool HasPendingActivity() const override; |
| |
| void Trace(Visitor*) const override; |
| |
| // Enable/disable throttling of visibility checking, so we don't have to add |
| // sleep() calls to tests to wait for notifications to show up. |
| static void SetThrottleDelayEnabledForTesting(bool); |
| |
| const HeapLinkedHashSet<WeakMember<IntersectionObservation>>& Observations() { |
| return observations_; |
| } |
| |
| private: |
| bool NeedsDelivery() const { return !active_observations_.empty(); } |
| void ProcessCustomWeakness(const LivenessBroker&); |
| |
| const Member<IntersectionObserverDelegate> delegate_; |
| |
| // See: `GetUkmMetricId()`. |
| const std::optional<LocalFrameUkmAggregator::MetricId> ukm_metric_id_; |
| |
| // We use UntracedMember<> here to do custom weak processing. |
| UntracedMember<Node> root_; |
| |
| HeapLinkedHashSet<WeakMember<IntersectionObservation>> observations_; |
| // Observations that have updates waiting to be delivered |
| HeapHashSet<Member<IntersectionObservation>> active_observations_; |
| const Vector<float> thresholds_; |
| const base::TimeDelta delay_; |
| const Vector<Length> margin_; |
| const Vector<Length> scroll_margin_; |
| const MarginTarget margin_target_; |
| const unsigned root_is_implicit_ : 1; |
| const unsigned track_visibility_ : 1; |
| const unsigned track_fraction_of_root_ : 1; |
| const unsigned always_report_root_bounds_ : 1; |
| const unsigned use_overflow_clip_edge_ : 1; |
| const unsigned expose_occluder_id_ : 1; |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_H_ |