// Copyright 2018 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_LAYOUT_LAYOUT_SHIFT_TRACKER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SHIFT_TRACKER_H_ #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_shift_region.h" #include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/platform/geometry/region.h" #include "third_party/blink/renderer/platform/graphics/dom_node_id.h" #include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" namespace blink { class IntRect; class LayoutObject; class LocalFrameView; class PropertyTreeState; class TracedValue; class WebInputEvent; // Tracks "layout shifts" from layout objects changing their visual location // between animation frames. See https://github.com/WICG/layout-instability. class CORE_EXPORT LayoutShiftTracker { USING_FAST_MALLOC(LayoutShiftTracker); public: LayoutShiftTracker(LocalFrameView*); ~LayoutShiftTracker() {} // |paint_offset_diff| is an additional amount by which the paint offset // shifted that is not tracked in visual rects. Visual rects are in the // local transform space of the LayoutObject. Any time the transform space is // changed, the offset of that rect to the "origin" is reset. This offset // is also known as the paint offset. // In cases where we can communicate paint offset diffs across transform // space change boundaries, |paint_offset_diff| is how to do it. In // particular, many transform spaces are artificial and are used as an // implementation detail of compositing to make it easier to isolate state for // composited layers. We can easily pass the paint offset diff across such // boundaries. void NotifyObjectPrePaint(const LayoutObject& object, const PropertyTreeState& property_tree_state, const IntRect& old_visual_rect, const IntRect& new_visual_rect, FloatSize paint_offset_delta); void NotifyPrePaintFinished(); void NotifyInput(const WebInputEvent&); void NotifyScroll(mojom::blink::ScrollType, ScrollOffset delta); void NotifyViewportSizeChanged(); bool IsActive(); double Score() const { return score_; } double WeightedScore() const { return weighted_score_; } float OverallMaxDistance() const { return overall_max_distance_; } bool ObservedInputOrScroll() const { return observed_input_or_scroll_; } void Dispose() { timer_.Stop(); } base::TimeTicks MostRecentInputTimestamp() { return most_recent_input_timestamp_; } // Saves and restores visual rects on layout objects when a layout tree is // rebuilt by Node::ReattachLayoutTree. class ReattachHook : public GarbageCollected { public: ReattachHook() : scope_(nullptr) {} void Trace(Visitor*); class Scope { public: Scope(const Node&); ~Scope(); private: bool active_; Scope* outer_; }; static void NotifyDetach(const Node&); static void NotifyAttach(const Node&); private: Scope* scope_; HeapHashMap, IntRect> visual_rects_; }; private: void ObjectShifted(const LayoutObject&, const PropertyTreeState&, FloatRect old_rect, FloatRect new_rect, FloatSize paint_offset_diff); void ReportShift(double score_delta, double weighted_score_delta); void TimerFired(TimerBase*) {} std::unique_ptr PerFrameTraceData(double score_delta, bool input_detected) const; void AttributionsToTracedValue(TracedValue&) const; double SubframeWeightingFactor() const; void SetLayoutShiftRects(const Vector& int_rects); void UpdateInputTimestamp(base::TimeTicks timestamp); // This owns us. UntracedMember frame_view_; // The document cumulative layout shift (DCLS) score for this LocalFrame, // unweighted, with move distance applied. double score_; // The cumulative layout shift score for this LocalFrame, with each increase // weighted by the extent to which the LocalFrame visibly occupied the main // frame at the time the shift occurred, e.g. x0.5 if the subframe occupied // half of the main frame's reported size; see SubframeWeightingFactor(). double weighted_score_; // Stores information related to buffering layout shifts after pointerdown. // We accumulate score deltas in this object until we know whether the // pointerdown should be treated as a tap (triggering layout shift exclusion) // or a scroll (not triggering layout shift exclusion). Once the correct // treatment is known, the pending layout shifts are reported appropriately // and the PointerdownPendingData object is reset. struct PointerdownPendingData { PointerdownPendingData() : saw_pointerdown(false), score_delta(0), weighted_score_delta(0) {} bool saw_pointerdown; double score_delta; double weighted_score_delta; }; PointerdownPendingData pointerdown_pending_data_; // The per-animation-frame impact region. LayoutShiftRegion region_; // Tracks the short period after an input event during which we ignore shifts // for the purpose of cumulative scoring, and report them to the web perf API // with hadRecentInput == true. TaskRunnerTimer timer_; // The maximum distance any layout object has moved in the current animation // frame. float frame_max_distance_; // The maximum distance any layout object has moved, across all animation // frames. float overall_max_distance_; // Sum of all scroll deltas that occurred in the current animation frame. ScrollOffset frame_scroll_delta_; // Whether either a user input or document scroll have been observed during // the session. (This is only tracked so UkmPageLoadMetricsObserver to report // LayoutInstability.CumulativeShiftScore.MainFrame.BeforeInputOrScroll. It's // not related to input exclusion or the LayoutShift::had_recent_input_ bit.) bool observed_input_or_scroll_; // Most recent timestamp of a user input event that has been observed. // User input includes window resizing but not scrolling. base::TimeTicks most_recent_input_timestamp_; bool most_recent_input_timestamp_initialized_; struct Attribution { DOMNodeId node_id; IntRect old_visual_rect; IntRect new_visual_rect; Attribution(); Attribution(DOMNodeId node_id, IntRect old_visual_rect, IntRect new_visual_rect); explicit operator bool() const; bool Encloses(const Attribution&) const; bool MoreImpactfulThan(const Attribution&) const; int Area() const; }; static constexpr int kMaxAttributions = 5; void MaybeRecordAttribution(const Attribution&); // Nodes that have contributed to the impact region for the current frame, for // use in trace event. Only populated while tracing. std::array attributions_; }; } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SHIFT_TRACKER_H_