OLD | NEW |
| (Empty) |
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 "components/scheduler/renderer/user_model.h" | |
6 | |
7 #include "base/metrics/histogram_macros.h" | |
8 | |
9 namespace scheduler { | |
10 | |
11 namespace { | |
12 // This enum is used to back a histogram, and should therefore be treated as | |
13 // append-only. | |
14 enum GesturePredictionResult { | |
15 GESTURE_OCCURED_WAS_PREDICTED = 0, | |
16 GESTURE_OCCURED_BUT_NOT_PREDICTED = 1, | |
17 GESTURE_PREDICTED_BUT_DID_NOT_OCCUR = 2, | |
18 GESTURE_PREDICTION_RESULT_COUNT = 3 | |
19 }; | |
20 | |
21 void RecordGesturePrediction(GesturePredictionResult result) { | |
22 UMA_HISTOGRAM_ENUMERATION( | |
23 "RendererScheduler.UserModel.GesturePredictedCorrectly", result, | |
24 GESTURE_PREDICTION_RESULT_COUNT); | |
25 } | |
26 | |
27 } // namespace | |
28 | |
29 UserModel::UserModel() | |
30 : pending_input_event_count_(0), | |
31 is_gesture_active_(false), | |
32 is_gesture_expected_(false) {} | |
33 UserModel::~UserModel() {} | |
34 | |
35 void UserModel::DidStartProcessingInputEvent(blink::WebInputEvent::Type type, | |
36 const base::TimeTicks now) { | |
37 last_input_signal_time_ = now; | |
38 if (type == blink::WebInputEvent::TouchStart || | |
39 type == blink::WebInputEvent::GestureScrollBegin || | |
40 type == blink::WebInputEvent::GesturePinchBegin) { | |
41 // Only update stats once per gesture. | |
42 if (!is_gesture_active_) { | |
43 last_gesture_start_time_ = now; | |
44 | |
45 RecordGesturePrediction(is_gesture_expected_ | |
46 ? GESTURE_OCCURED_WAS_PREDICTED | |
47 : GESTURE_OCCURED_BUT_NOT_PREDICTED); | |
48 | |
49 if (!last_reset_time_.is_null()) { | |
50 base::TimeDelta time_since_reset = now - last_reset_time_; | |
51 UMA_HISTOGRAM_MEDIUM_TIMES( | |
52 "RendererScheduler.UserModel.GestureStartTimeSinceModelReset", | |
53 time_since_reset); | |
54 } | |
55 | |
56 // If there has been a previous gesture, record a UMA metric for the time | |
57 // interval between then and now. | |
58 if (!last_continuous_gesture_time_.is_null()) { | |
59 base::TimeDelta time_since_last_gesture = | |
60 now - last_continuous_gesture_time_; | |
61 UMA_HISTOGRAM_MEDIUM_TIMES( | |
62 "RendererScheduler.UserModel.TimeBetweenGestures", | |
63 time_since_last_gesture); | |
64 } | |
65 } | |
66 | |
67 is_gesture_active_ = true; | |
68 } | |
69 | |
70 // We need to track continuous gestures seperatly for scroll detection | |
71 // because taps should not be confused with scrolls. | |
72 if (type == blink::WebInputEvent::GestureScrollBegin || | |
73 type == blink::WebInputEvent::GestureScrollEnd || | |
74 type == blink::WebInputEvent::GestureScrollUpdate || | |
75 type == blink::WebInputEvent::GestureFlingStart || | |
76 type == blink::WebInputEvent::GestureFlingCancel || | |
77 type == blink::WebInputEvent::GesturePinchBegin || | |
78 type == blink::WebInputEvent::GesturePinchEnd || | |
79 type == blink::WebInputEvent::GesturePinchUpdate) { | |
80 last_continuous_gesture_time_ = now; | |
81 } | |
82 | |
83 // If the gesture has ended, clear |is_gesture_active_| and record a UMA | |
84 // metric that tracks its duration. | |
85 if (type == blink::WebInputEvent::GestureScrollEnd || | |
86 type == blink::WebInputEvent::GesturePinchEnd || | |
87 type == blink::WebInputEvent::GestureFlingStart || | |
88 type == blink::WebInputEvent::TouchEnd) { | |
89 // Only update stats once per gesture. | |
90 if (is_gesture_active_) { | |
91 base::TimeDelta duration = now - last_gesture_start_time_; | |
92 UMA_HISTOGRAM_TIMES("RendererScheduler.UserModel.GestureDuration", | |
93 duration); | |
94 } | |
95 is_gesture_active_ = false; | |
96 } | |
97 | |
98 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
99 "is_gesture_active", is_gesture_active_); | |
100 | |
101 pending_input_event_count_++; | |
102 } | |
103 | |
104 void UserModel::DidFinishProcessingInputEvent(const base::TimeTicks now) { | |
105 last_input_signal_time_ = now; | |
106 if (pending_input_event_count_ > 0) | |
107 pending_input_event_count_--; | |
108 } | |
109 | |
110 base::TimeDelta UserModel::TimeLeftInUserGesture(base::TimeTicks now) const { | |
111 base::TimeDelta escalated_priority_duration = | |
112 base::TimeDelta::FromMilliseconds(kGestureEstimationLimitMillis); | |
113 | |
114 // If the input event is still pending, go into input prioritized policy and | |
115 // check again later. | |
116 if (pending_input_event_count_ > 0) | |
117 return escalated_priority_duration; | |
118 if (last_input_signal_time_.is_null() || | |
119 last_input_signal_time_ + escalated_priority_duration < now) { | |
120 return base::TimeDelta(); | |
121 } | |
122 return last_input_signal_time_ + escalated_priority_duration - now; | |
123 } | |
124 | |
125 bool UserModel::IsGestureExpectedSoon( | |
126 const base::TimeTicks now, | |
127 base::TimeDelta* prediction_valid_duration) { | |
128 bool was_gesture_expected = is_gesture_expected_; | |
129 is_gesture_expected_ = | |
130 IsGestureExpectedSoonImpl(now, prediction_valid_duration); | |
131 | |
132 // Track when we start expecting a gesture so we can work out later if a | |
133 // gesture actually happened. | |
134 if (!was_gesture_expected && is_gesture_expected_) | |
135 last_gesture_expected_start_time_ = now; | |
136 | |
137 if (was_gesture_expected && !is_gesture_expected_ && | |
138 last_gesture_expected_start_time_ > last_gesture_start_time_) { | |
139 RecordGesturePrediction(GESTURE_PREDICTED_BUT_DID_NOT_OCCUR); | |
140 } | |
141 return is_gesture_expected_; | |
142 } | |
143 | |
144 bool UserModel::IsGestureExpectedSoonImpl( | |
145 const base::TimeTicks now, | |
146 base::TimeDelta* prediction_valid_duration) const { | |
147 if (is_gesture_active_) { | |
148 if (IsGestureExpectedToContinue(now, prediction_valid_duration)) { | |
149 return false; | |
150 } else { | |
151 // If a gesture is not expected to continue then we expect a subsequent | |
152 // gesture soon. | |
153 *prediction_valid_duration = | |
154 base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis); | |
155 return true; | |
156 } | |
157 } else { | |
158 // If we've have a finished a gesture then a subsequent gesture is deemed | |
159 // likely. | |
160 base::TimeDelta expect_subsequent_gesture_for = | |
161 base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis); | |
162 if (last_continuous_gesture_time_.is_null() || | |
163 last_continuous_gesture_time_ + expect_subsequent_gesture_for <= now) { | |
164 return false; | |
165 } | |
166 *prediction_valid_duration = | |
167 last_continuous_gesture_time_ + expect_subsequent_gesture_for - now; | |
168 return true; | |
169 } | |
170 } | |
171 | |
172 bool UserModel::IsGestureExpectedToContinue( | |
173 const base::TimeTicks now, | |
174 base::TimeDelta* prediction_valid_duration) const { | |
175 if (!is_gesture_active_) | |
176 return false; | |
177 | |
178 base::TimeDelta median_gesture_duration = | |
179 base::TimeDelta::FromMilliseconds(kMedianGestureDurationMillis); | |
180 base::TimeTicks expected_gesture_end_time = | |
181 last_gesture_start_time_ + median_gesture_duration; | |
182 | |
183 if (expected_gesture_end_time > now) { | |
184 *prediction_valid_duration = expected_gesture_end_time - now; | |
185 return true; | |
186 } | |
187 return false; | |
188 } | |
189 | |
190 void UserModel::Reset(base::TimeTicks now) { | |
191 last_input_signal_time_ = base::TimeTicks(); | |
192 last_gesture_start_time_ = base::TimeTicks(); | |
193 last_continuous_gesture_time_ = base::TimeTicks(); | |
194 last_gesture_expected_start_time_ = base::TimeTicks(); | |
195 last_reset_time_ = now; | |
196 is_gesture_active_ = false; | |
197 is_gesture_expected_ = false; | |
198 } | |
199 | |
200 void UserModel::AsValueInto(base::trace_event::TracedValue* state) const { | |
201 state->BeginDictionary("user_model"); | |
202 state->SetInteger("pending_input_event_count", pending_input_event_count_); | |
203 state->SetDouble( | |
204 "last_input_signal_time", | |
205 (last_input_signal_time_ - base::TimeTicks()).InMillisecondsF()); | |
206 state->SetDouble( | |
207 "last_gesture_start_time", | |
208 (last_gesture_start_time_ - base::TimeTicks()).InMillisecondsF()); | |
209 state->SetDouble( | |
210 "last_continuous_gesture_time", | |
211 (last_continuous_gesture_time_ - base::TimeTicks()).InMillisecondsF()); | |
212 state->SetDouble("last_gesture_expected_start_time", | |
213 (last_gesture_expected_start_time_ - base::TimeTicks()) | |
214 .InMillisecondsF()); | |
215 state->SetDouble("last_reset_time", | |
216 (last_reset_time_ - base::TimeTicks()).InMillisecondsF()); | |
217 state->SetBoolean("is_gesture_expected", is_gesture_expected_); | |
218 state->SetBoolean("is_gesture_active", is_gesture_active_); | |
219 state->EndDictionary(); | |
220 } | |
221 | |
222 } // namespace scheduler | |
OLD | NEW |