blob: 361b57ef7061e035501772fc5767f3ca62aa1fff [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.
#include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
namespace blink {
namespace scheduler {
UserModel::UserModel()
: pending_input_event_count_(0),
is_gesture_active_(false),
is_gesture_expected_(false) {}
UserModel::~UserModel() = default;
void UserModel::DidStartProcessingInputEvent(blink::WebInputEvent::Type type,
const base::TimeTicks now) {
last_input_signal_time_ = now;
if (type == blink::WebInputEvent::kTouchStart ||
type == blink::WebInputEvent::kGestureScrollBegin ||
type == blink::WebInputEvent::kGesturePinchBegin) {
// Only update stats once per gesture.
if (!is_gesture_active_)
last_gesture_start_time_ = now;
is_gesture_active_ = true;
}
// We need to track continuous gestures seperatly for scroll detection
// because taps should not be confused with scrolls.
if (type == blink::WebInputEvent::kGestureScrollBegin ||
type == blink::WebInputEvent::kGestureScrollEnd ||
type == blink::WebInputEvent::kGestureScrollUpdate ||
type == blink::WebInputEvent::kGestureFlingStart ||
type == blink::WebInputEvent::kGestureFlingCancel ||
type == blink::WebInputEvent::kGesturePinchBegin ||
type == blink::WebInputEvent::kGesturePinchEnd ||
type == blink::WebInputEvent::kGesturePinchUpdate) {
last_continuous_gesture_time_ = now;
}
// If the gesture has ended, clear |is_gesture_active_| and record a UMA
// metric that tracks its duration.
if (type == blink::WebInputEvent::kGestureScrollEnd ||
type == blink::WebInputEvent::kGesturePinchEnd ||
type == blink::WebInputEvent::kGestureFlingStart ||
type == blink::WebInputEvent::kTouchEnd) {
is_gesture_active_ = false;
}
TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
"is_gesture_active", is_gesture_active_);
pending_input_event_count_++;
}
void UserModel::DidFinishProcessingInputEvent(const base::TimeTicks now) {
last_input_signal_time_ = now;
if (pending_input_event_count_ > 0)
pending_input_event_count_--;
}
base::TimeDelta UserModel::TimeLeftInUserGesture(base::TimeTicks now) const {
base::TimeDelta escalated_priority_duration =
base::TimeDelta::FromMilliseconds(kGestureEstimationLimitMillis);
// If the input event is still pending, go into input prioritized policy and
// check again later.
if (pending_input_event_count_ > 0)
return escalated_priority_duration;
if (last_input_signal_time_.is_null() ||
last_input_signal_time_ + escalated_priority_duration < now) {
return base::TimeDelta();
}
return last_input_signal_time_ + escalated_priority_duration - now;
}
bool UserModel::IsGestureExpectedSoon(
const base::TimeTicks now,
base::TimeDelta* prediction_valid_duration) {
bool was_gesture_expected = is_gesture_expected_;
is_gesture_expected_ =
IsGestureExpectedSoonImpl(now, prediction_valid_duration);
// Track when we start expecting a gesture so we can work out later if a
// gesture actually happened.
if (!was_gesture_expected && is_gesture_expected_)
last_gesture_expected_start_time_ = now;
return is_gesture_expected_;
}
bool UserModel::IsGestureExpectedSoonImpl(
const base::TimeTicks now,
base::TimeDelta* prediction_valid_duration) const {
if (is_gesture_active_) {
if (IsGestureExpectedToContinue(now, prediction_valid_duration))
return false;
*prediction_valid_duration =
base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
return true;
} else {
// If we've have a finished a gesture then a subsequent gesture is deemed
// likely.
base::TimeDelta expect_subsequent_gesture_for =
base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
if (last_continuous_gesture_time_.is_null() ||
last_continuous_gesture_time_ + expect_subsequent_gesture_for <= now) {
return false;
}
*prediction_valid_duration =
last_continuous_gesture_time_ + expect_subsequent_gesture_for - now;
return true;
}
}
bool UserModel::IsGestureExpectedToContinue(
const base::TimeTicks now,
base::TimeDelta* prediction_valid_duration) const {
if (!is_gesture_active_)
return false;
base::TimeDelta median_gesture_duration =
base::TimeDelta::FromMilliseconds(kMedianGestureDurationMillis);
base::TimeTicks expected_gesture_end_time =
last_gesture_start_time_ + median_gesture_duration;
if (expected_gesture_end_time > now) {
*prediction_valid_duration = expected_gesture_end_time - now;
return true;
}
return false;
}
void UserModel::Reset(base::TimeTicks now) {
last_input_signal_time_ = base::TimeTicks();
last_gesture_start_time_ = base::TimeTicks();
last_continuous_gesture_time_ = base::TimeTicks();
last_gesture_expected_start_time_ = base::TimeTicks();
last_reset_time_ = now;
is_gesture_active_ = false;
is_gesture_expected_ = false;
pending_input_event_count_ = 0;
}
void UserModel::AsValueInto(base::trace_event::TracedValue* state) const {
state->BeginDictionary("user_model");
state->SetInteger("pending_input_event_count", pending_input_event_count_);
state->SetDouble(
"last_input_signal_time",
(last_input_signal_time_ - base::TimeTicks()).InMillisecondsF());
state->SetDouble(
"last_gesture_start_time",
(last_gesture_start_time_ - base::TimeTicks()).InMillisecondsF());
state->SetDouble(
"last_continuous_gesture_time",
(last_continuous_gesture_time_ - base::TimeTicks()).InMillisecondsF());
state->SetDouble("last_gesture_expected_start_time",
(last_gesture_expected_start_time_ - base::TimeTicks())
.InMillisecondsF());
state->SetDouble("last_reset_time",
(last_reset_time_ - base::TimeTicks()).InMillisecondsF());
state->SetBoolean("is_gesture_expected", is_gesture_expected_);
state->SetBoolean("is_gesture_active", is_gesture_active_);
state->EndDictionary();
}
} // namespace scheduler
} // namespace blink