1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
6 
7 namespace blink {
8 namespace scheduler {
9 
UserModel()10 UserModel::UserModel()
11     : pending_input_event_count_(0),
12       is_gesture_active_(false),
13       is_gesture_expected_(false) {}
14 
DidStartProcessingInputEvent(blink::WebInputEvent::Type type,const base::TimeTicks now)15 void UserModel::DidStartProcessingInputEvent(blink::WebInputEvent::Type type,
16                                              const base::TimeTicks now) {
17   last_input_signal_time_ = now;
18   if (type == blink::WebInputEvent::Type::kTouchStart ||
19       type == blink::WebInputEvent::Type::kGestureScrollBegin ||
20       type == blink::WebInputEvent::Type::kGesturePinchBegin) {
21     // Only update stats once per gesture.
22     if (!is_gesture_active_)
23       last_gesture_start_time_ = now;
24 
25     is_gesture_active_ = true;
26   }
27 
28   // We need to track continuous gestures seperatly for scroll detection
29   // because taps should not be confused with scrolls.
30   if (type == blink::WebInputEvent::Type::kGestureScrollBegin ||
31       type == blink::WebInputEvent::Type::kGestureScrollEnd ||
32       type == blink::WebInputEvent::Type::kGestureScrollUpdate ||
33       type == blink::WebInputEvent::Type::kGestureFlingStart ||
34       type == blink::WebInputEvent::Type::kGestureFlingCancel ||
35       type == blink::WebInputEvent::Type::kGesturePinchBegin ||
36       type == blink::WebInputEvent::Type::kGesturePinchEnd ||
37       type == blink::WebInputEvent::Type::kGesturePinchUpdate) {
38     last_continuous_gesture_time_ = now;
39   }
40 
41   // If the gesture has ended, clear |is_gesture_active_| and record a UMA
42   // metric that tracks its duration.
43   if (type == blink::WebInputEvent::Type::kGestureScrollEnd ||
44       type == blink::WebInputEvent::Type::kGesturePinchEnd ||
45       type == blink::WebInputEvent::Type::kGestureFlingStart ||
46       type == blink::WebInputEvent::Type::kTouchEnd) {
47     is_gesture_active_ = false;
48   }
49 
50   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
51                  "is_gesture_active", is_gesture_active_);
52 
53   pending_input_event_count_++;
54 }
55 
DidFinishProcessingInputEvent(const base::TimeTicks now)56 void UserModel::DidFinishProcessingInputEvent(const base::TimeTicks now) {
57   last_input_signal_time_ = now;
58   if (pending_input_event_count_ > 0)
59     pending_input_event_count_--;
60 }
61 
TimeLeftInUserGesture(base::TimeTicks now) const62 base::TimeDelta UserModel::TimeLeftInUserGesture(base::TimeTicks now) const {
63   base::TimeDelta escalated_priority_duration =
64       base::TimeDelta::FromMilliseconds(kGestureEstimationLimitMillis);
65 
66   // If the input event is still pending, go into input prioritized policy and
67   // check again later.
68   if (pending_input_event_count_ > 0)
69     return escalated_priority_duration;
70   if (last_input_signal_time_.is_null() ||
71       last_input_signal_time_ + escalated_priority_duration < now) {
72     return base::TimeDelta();
73   }
74   return last_input_signal_time_ + escalated_priority_duration - now;
75 }
76 
IsGestureExpectedSoon(const base::TimeTicks now,base::TimeDelta * prediction_valid_duration)77 bool UserModel::IsGestureExpectedSoon(
78     const base::TimeTicks now,
79     base::TimeDelta* prediction_valid_duration) {
80   bool was_gesture_expected = is_gesture_expected_;
81   is_gesture_expected_ =
82       IsGestureExpectedSoonImpl(now, prediction_valid_duration);
83 
84   // Track when we start expecting a gesture so we can work out later if a
85   // gesture actually happened.
86   if (!was_gesture_expected && is_gesture_expected_)
87     last_gesture_expected_start_time_ = now;
88   return is_gesture_expected_;
89 }
90 
IsGestureExpectedSoonImpl(const base::TimeTicks now,base::TimeDelta * prediction_valid_duration) const91 bool UserModel::IsGestureExpectedSoonImpl(
92     const base::TimeTicks now,
93     base::TimeDelta* prediction_valid_duration) const {
94   if (is_gesture_active_) {
95     if (IsGestureExpectedToContinue(now, prediction_valid_duration))
96       return false;
97     *prediction_valid_duration =
98         base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
99     return true;
100   } else {
101     // If we have finished a gesture then a subsequent gesture is deemed likely.
102     base::TimeDelta expect_subsequent_gesture_for =
103         base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
104     if (last_continuous_gesture_time_.is_null() ||
105         last_continuous_gesture_time_ + expect_subsequent_gesture_for <= now) {
106       return false;
107     }
108     *prediction_valid_duration =
109         last_continuous_gesture_time_ + expect_subsequent_gesture_for - now;
110     return true;
111   }
112 }
113 
IsGestureExpectedToContinue(const base::TimeTicks now,base::TimeDelta * prediction_valid_duration) const114 bool UserModel::IsGestureExpectedToContinue(
115     const base::TimeTicks now,
116     base::TimeDelta* prediction_valid_duration) const {
117   if (!is_gesture_active_)
118     return false;
119 
120   base::TimeDelta median_gesture_duration =
121       base::TimeDelta::FromMilliseconds(kMedianGestureDurationMillis);
122   base::TimeTicks expected_gesture_end_time =
123       last_gesture_start_time_ + median_gesture_duration;
124 
125   if (expected_gesture_end_time > now) {
126     *prediction_valid_duration = expected_gesture_end_time - now;
127     return true;
128   }
129   return false;
130 }
131 
Reset(base::TimeTicks now)132 void UserModel::Reset(base::TimeTicks now) {
133   last_input_signal_time_ = base::TimeTicks();
134   last_gesture_start_time_ = base::TimeTicks();
135   last_continuous_gesture_time_ = base::TimeTicks();
136   last_gesture_expected_start_time_ = base::TimeTicks();
137   last_reset_time_ = now;
138   is_gesture_active_ = false;
139   is_gesture_expected_ = false;
140   pending_input_event_count_ = 0;
141 }
142 
AsValueInto(base::trace_event::TracedValue * state) const143 void UserModel::AsValueInto(base::trace_event::TracedValue* state) const {
144   auto dictionary_scope = state->BeginDictionaryScoped("user_model");
145   state->SetInteger("pending_input_event_count", pending_input_event_count_);
146   state->SetDouble(
147       "last_input_signal_time",
148       (last_input_signal_time_ - base::TimeTicks()).InMillisecondsF());
149   state->SetDouble(
150       "last_gesture_start_time",
151       (last_gesture_start_time_ - base::TimeTicks()).InMillisecondsF());
152   state->SetDouble(
153       "last_continuous_gesture_time",
154       (last_continuous_gesture_time_ - base::TimeTicks()).InMillisecondsF());
155   state->SetDouble("last_gesture_expected_start_time",
156                    (last_gesture_expected_start_time_ - base::TimeTicks())
157                        .InMillisecondsF());
158   state->SetDouble("last_reset_time",
159                    (last_reset_time_ - base::TimeTicks()).InMillisecondsF());
160   state->SetBoolean("is_gesture_expected", is_gesture_expected_);
161   state->SetBoolean("is_gesture_active", is_gesture_active_);
162 }
163 
164 }  // namespace scheduler
165 }  // namespace blink
166