blob: 212a2d4a32ec2a72b98f6cdd434d1b9019ab7f53 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/input/scroll_timing_controller.h"
#include <utility>
namespace cc {
ScrollTimingController::ScrollTimingController() = default;
ScrollTimingController::~ScrollTimingController() = default;
void ScrollTimingController::DidScrollBegin(ui::ScrollInputType input_type,
base::TimeTicks event_timestamp) {
if (input_type != ui::ScrollInputType::kTouchscreen &&
input_type != ui::ScrollInputType::kWheel) {
return;
}
// Without a hardware timestamp, startTime/duration would not be comparable
// with the rest of the dataset, so skip tracking this gesture.
if (event_timestamp.is_null()) {
return;
}
// `element_id` is captured later in `DidScrollUpdate`; `end_time` stays
// nullopt until the gesture finishes. A second DidScrollBegin without a
// matching DidScrollEnd replaces any in-flight gesture.
active_info_ =
ScrollTimingInfo{.start_time = event_timestamp, .input_type = input_type};
}
void ScrollTimingController::DidScrollUpdate(ElementId element_id) {
// Only meaningful while a gesture is being tracked; first effective
// update of the gesture captures the latched scroller, later updates
// leave it alone.
if (!active_info_.has_value() || active_info_->element_id) {
return;
}
active_info_->element_id = element_id;
}
void ScrollTimingController::DidScrollEnd(ui::ScrollInputType input_type) {
if (!active_info_.has_value()) {
return;
}
// Defensive: a stale/wrong-device GestureScrollEnd may reach here before
// the InputHandlerProxy validates it against the active gesture. Drop the
// in-flight record too - the begin/end pair is inconsistent, so the
// tracked data is suspect and shouldn't be emitted.
if (input_type != active_info_->input_type) {
active_info_.reset();
return;
}
// A non-empty ElementId means at least one scroll update moved a scroller;
// only those gestures produce a PerformanceScrollTiming entry.
if (active_info_->element_id) {
active_info_->end_time = base::TimeTicks::Now();
completed_infos_.push_back(*active_info_);
}
active_info_.reset();
}
std::vector<ScrollTimingInfo>
ScrollTimingController::TakeCompletedScrollTimingInfos() {
return std::exchange(completed_infos_, {});
}
std::optional<base::TimeTicks>
ScrollTimingController::ActiveScrollStartForTesting() const {
return active_info_.has_value()
? std::optional<base::TimeTicks>(active_info_->start_time)
: std::nullopt;
}
} // namespace cc