Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(379)

Side by Side Diff: third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp

Issue 1470153004: Autoplay experiment metric fixes and additions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: back to using some of ::testing, since it works. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/html/AutoplayExperimentHelper.h" 5 #include "core/html/AutoplayExperimentHelper.h"
6 6
7 #include "core/dom/Document.h" 7 #include "core/dom/Document.h"
8 #include "core/frame/FrameView.h"
9 #include "core/frame/Settings.h" 8 #include "core/frame/Settings.h"
10 #include "core/html/HTMLMediaElement.h" 9 #include "core/html/HTMLMediaElement.h"
11 #include "core/layout/LayoutBox.h" 10 #include "core/layout/LayoutBox.h"
12 #include "core/layout/LayoutObject.h" 11 #include "core/layout/LayoutObject.h"
13 #include "core/layout/LayoutVideo.h" 12 #include "core/layout/LayoutVideo.h"
14 #include "core/layout/LayoutView.h" 13 #include "core/layout/LayoutView.h"
15 #include "core/page/Page.h" 14 #include "core/page/Page.h"
16 #include "platform/Logging.h" 15 #include "platform/Logging.h"
17 #include "platform/UserGestureIndicator.h" 16 #include "platform/UserGestureIndicator.h"
18 #include "platform/geometry/IntRect.h" 17 #include "platform/geometry/IntRect.h"
19 18
20 namespace blink { 19 namespace blink {
21 20
22 using namespace HTMLNames; 21 using namespace HTMLNames;
23 22
24 // Seconds to wait after a video has stopped moving before playing it. 23 // Seconds to wait after a video has stopped moving before playing it.
25 static const double kViewportTimerPollDelay = 0.5; 24 static const double kViewportTimerPollDelay = 0.5;
26 25
27 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element) 26 AutoplayExperimentHelper::AutoplayExperimentHelper(Client& client)
28 : m_element(&element) 27 : m_client(client)
29 , m_mode(Mode::ExperimentOff) 28 , m_mode(Mode::ExperimentOff)
30 , m_playPending(false) 29 , m_playPending(false)
31 , m_registeredWithLayoutObject(false) 30 , m_registeredWithLayoutObject(false)
32 , m_wasInViewport(false) 31 , m_wasInViewport(false)
32 , m_autoplayMediaEncountered(false)
33 , m_playbackStartedMetricRecorded(false)
34 , m_waitingForAutoplayPlaybackEnd(false)
35 , m_recordedElement(false)
33 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity()) 36 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity())
34 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired) 37 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired)
38 , m_autoplayDeferredMetric(GesturelessPlaybackNotOverridden)
35 { 39 {
36 if (document().settings()) { 40 m_mode = fromString(this->client().autoplayExperimentMode());
37 m_mode = fromString(document().settings()->autoplayExperimentMode());
38 41
39 if (m_mode != Mode::ExperimentOff) { 42 if (m_mode != Mode::ExperimentOff) {
40 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d", 43 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d",
41 m_mode); 44 m_mode);
42 }
43 } 45 }
44 } 46 }
45 47
46 AutoplayExperimentHelper::~AutoplayExperimentHelper() 48 AutoplayExperimentHelper::~AutoplayExperimentHelper()
47 { 49 {
48 unregisterForPositionUpdatesIfNeeded(); 50 unregisterForPositionUpdatesIfNeeded();
49 } 51 }
50 52
51 void AutoplayExperimentHelper::becameReadyToPlay() 53 void AutoplayExperimentHelper::becameReadyToPlay()
52 { 54 {
53 // Assuming that we're eligible to override the user gesture requirement, 55 // Assuming that we're eligible to override the user gesture requirement,
54 // either play if we meet the visibility checks, or install a listener 56 // either play if we meet the visibility checks, or install a listener
55 // to wait for them to pass. 57 // to wait for them to pass. We do not actually start playback; our
58 // caller must do that.
59 autoplayMediaEncountered();
60
56 if (isEligible()) { 61 if (isEligible()) {
57 if (meetsVisibilityRequirements()) 62 if (meetsVisibilityRequirements())
58 prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately); 63 prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediatel y);
59 else 64 else
60 registerForPositionUpdatesIfNeeded(); 65 registerForPositionUpdatesIfNeeded();
61 } 66 }
62 } 67 }
63 68
64 void AutoplayExperimentHelper::playMethodCalled() 69 void AutoplayExperimentHelper::playMethodCalled()
65 { 70 {
66 // Set the pending state, even if the play isn't going to be pending. 71 // Set the pending state, even if the play isn't going to be pending.
67 // Eligibility can change if, for example, the mute status changes. 72 // Eligibility can change if, for example, the mute status changes.
68 // Having this set is okay. 73 // Having this set is okay.
69 m_playPending = true; 74 m_playPending = true;
70 75
71 if (!UserGestureIndicator::processingUserGesture()) { 76 if (!UserGestureIndicator::processingUserGesture()) {
77 autoplayMediaEncountered();
72 78
73 if (isEligible()) { 79 if (isEligible()) {
74 // Remember that userGestureRequiredForPlay is required for 80 // Remember that userGestureRequiredForPlay is required for
75 // us to be eligible for the experiment. 81 // us to be eligible for the experiment.
76 // If we are able to override the gesture requirement now, then 82 // If we are able to override the gesture requirement now, then
77 // do so. Otherwise, install an event listener if we need one. 83 // do so. Otherwise, install an event listener if we need one.
78 if (meetsVisibilityRequirements()) { 84 if (meetsVisibilityRequirements()) {
79 // Override the gesture and play. 85 // Override the gesture and assume that play() will succeed.
80 prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately) ; 86 prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediat ely);
81 } else { 87 } else {
82 // Wait for viewport visibility. 88 // Wait for viewport visibility.
83 registerForPositionUpdatesIfNeeded(); 89 registerForPositionUpdatesIfNeeded();
84 } 90 }
85 } 91 }
92 } else if (isUserGestureRequiredForPlay()) {
93 // If this media tried to autoplay, and we haven't played it yet, then
94 // record that the user provided the gesture to start it the first time.
95 if (m_autoplayMediaEncountered && !m_playbackStartedMetricRecorded)
96 recordAutoplayMetric(AutoplayManualStart);
97 // Don't let future gestureless playbacks affect metrics.
98 m_autoplayMediaEncountered = true;
99 m_playbackStartedMetricRecorded = true;
86 100
87 } else if (element().isUserGestureRequiredForPlay()) {
88 unregisterForPositionUpdatesIfNeeded(); 101 unregisterForPositionUpdatesIfNeeded();
89 } 102 }
90 } 103 }
91 104
92 void AutoplayExperimentHelper::pauseMethodCalled() 105 void AutoplayExperimentHelper::pauseMethodCalled()
93 { 106 {
94 // Don't try to autoplay, if we would have. 107 // Don't try to autoplay, if we would have.
95 m_playPending = false; 108 m_playPending = false;
96 unregisterForPositionUpdatesIfNeeded(); 109 unregisterForPositionUpdatesIfNeeded();
110
111 if (!client().paused())
112 recordMetricsBeforePause();
113 }
114
115 void AutoplayExperimentHelper::loadMethodCalled()
116 {
117 if (!client().paused())
118 recordMetricsBeforePause();
119
120 if (UserGestureIndicator::processingUserGesture() && isUserGestureRequiredFo rPlay()) {
121 recordAutoplayMetric(AutoplayEnabledThroughLoad);
122 removeUserGestureRequirement(GesturelessPlaybackEnabledByLoad);
123 }
97 } 124 }
98 125
99 void AutoplayExperimentHelper::mutedChanged() 126 void AutoplayExperimentHelper::mutedChanged()
100 { 127 {
101 // If we are no longer eligible for the autoplay experiment, then also 128 // If we are no longer eligible for the autoplay experiment, then also
102 // quit listening for events. If we are eligible, and if we should be 129 // quit listening for events. If we are eligible, and if we should be
103 // playing, then start playing. In other words, start playing if 130 // playing, then start playing. In other words, start playing if
104 // we just needed 'mute' to autoplay. 131 // we just needed 'mute' to autoplay.
132
133 // Make sure that autoplay was actually deferred. If, for example, the
134 // autoplay attribute is set after the media is ready to play, then it
135 // would normally have no effect. We don't want to start playing.
136 if (!m_autoplayMediaEncountered)
137 return;
138
105 if (!isEligible()) { 139 if (!isEligible()) {
106 unregisterForPositionUpdatesIfNeeded(); 140 unregisterForPositionUpdatesIfNeeded();
107 } else { 141 } else {
108 // Try to play. If we can't, then install a listener. 142 // Try to play. If we can't, then install a listener.
109 if (!maybeStartPlaying()) 143 if (!maybeStartPlaying())
110 registerForPositionUpdatesIfNeeded(); 144 registerForPositionUpdatesIfNeeded();
111 } 145 }
112 } 146 }
113 147
114 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded() 148 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded()
115 { 149 {
116 // If we don't require that the player is in the viewport, then we don't 150 // If we don't require that the player is in the viewport, then we don't
117 // need the listener. 151 // need the listener.
118 if (!enabled(IfViewport)) { 152 if (!enabled(IfViewport)) {
119 if (!enabled(IfPageVisible)) 153 if (!enabled(IfPageVisible))
120 return; 154 return;
121 } 155 }
122 156
123 if (LayoutObject* layoutObject = element().layoutObject()) { 157 client().setRequestPositionUpdates(true);
124 LayoutMedia* layoutMedia = toLayoutMedia(layoutObject);
125 layoutMedia->setRequestPositionUpdates(true);
126 }
127 158
128 // Set this unconditionally, in case we have no layout object yet. 159 // Set this unconditionally, in case we have no layout object yet.
129 m_registeredWithLayoutObject = true; 160 m_registeredWithLayoutObject = true;
130 } 161 }
131 162
132 void AutoplayExperimentHelper::unregisterForPositionUpdatesIfNeeded() 163 void AutoplayExperimentHelper::unregisterForPositionUpdatesIfNeeded()
133 { 164 {
134 if (m_registeredWithLayoutObject) { 165 if (m_registeredWithLayoutObject)
135 if (LayoutObject* obj = element().layoutObject()) { 166 client().setRequestPositionUpdates(false);
136 LayoutMedia* layoutMedia = toLayoutMedia(obj);
137 layoutMedia->setRequestPositionUpdates(false);
138 }
139 }
140 167
141 // Clear this unconditionally so that we don't re-register if we didn't 168 // Clear this unconditionally so that we don't re-register if we didn't
142 // have a LayoutObject now, but get one later. 169 // have a LayoutObject now, but get one later.
143 m_registeredWithLayoutObject = false; 170 m_registeredWithLayoutObject = false;
144 } 171 }
145 172
146 void AutoplayExperimentHelper::positionChanged(const IntRect& visibleRect) 173 void AutoplayExperimentHelper::positionChanged(const IntRect& visibleRect)
147 { 174 {
148 // Something, maybe position, has changed. If applicable, start a 175 // Something, maybe position, has changed. If applicable, start a
149 // timer to look for the end of a scroll operation. 176 // timer to look for the end of a scroll operation.
150 // Don't do much work here. 177 // Don't do much work here.
151 // Also note that we are called quite often, including when the 178 // Also note that we are called quite often, including when the
152 // page becomes visible. That's why we don't bother to register 179 // page becomes visible. That's why we don't bother to register
153 // for page visibility changes explicitly. 180 // for page visibility changes explicitly.
181 if (visibleRect.isEmpty())
182 return;
154 183
155 m_lastVisibleRect = visibleRect; 184 m_lastVisibleRect = visibleRect;
156 185
157 if (!element().layoutObject()) 186 IntRect currentLocation = client().absoluteBoundingBoxRect();
187 if (currentLocation.isEmpty())
158 return; 188 return;
159 189
160 IntRect currentLocation = element().layoutObject()->absoluteBoundingBoxRect( );
161 bool inViewport = meetsVisibilityRequirements(); 190 bool inViewport = meetsVisibilityRequirements();
162 191
163 if (m_lastLocation != currentLocation) { 192 if (m_lastLocation != currentLocation) {
164 m_lastLocationUpdateTime = monotonicallyIncreasingTime(); 193 m_lastLocationUpdateTime = monotonicallyIncreasingTime();
165 m_lastLocation = currentLocation; 194 m_lastLocation = currentLocation;
166 } 195 }
167 196
168 if (inViewport && !m_wasInViewport) { 197 if (inViewport && !m_wasInViewport) {
169 // Only reset the timer when we transition from not visible to 198 // Only reset the timer when we transition from not visible to
170 // visible, because resetting the timer isn't cheap. 199 // visible, because resetting the timer isn't cheap.
171 m_viewportTimer.startOneShot(kViewportTimerPollDelay, BLINK_FROM_HERE); 200 m_viewportTimer.startOneShot(kViewportTimerPollDelay, BLINK_FROM_HERE);
172 } 201 }
173 m_wasInViewport = inViewport; 202 m_wasInViewport = inViewport;
174 } 203 }
175 204
176 void AutoplayExperimentHelper::updatePositionNotificationRegistration() 205 void AutoplayExperimentHelper::updatePositionNotificationRegistration()
177 { 206 {
178 if (m_registeredWithLayoutObject) { 207 if (m_registeredWithLayoutObject)
179 LayoutMedia* layoutMedia = toLayoutMedia(element().layoutObject()); 208 client().setRequestPositionUpdates(true);
180 layoutMedia->setRequestPositionUpdates(true);
181 }
182 } 209 }
183 210
184 void AutoplayExperimentHelper::triggerAutoplayViewportCheckForTesting() 211 void AutoplayExperimentHelper::triggerAutoplayViewportCheckForTesting()
185 { 212 {
186 FrameView* view = document().view();
187 if (view)
188 positionChanged(view->rootFrameToContents(view->computeVisibleArea()));
189
190 // Make sure that the last update appears to be sufficiently far in the 213 // Make sure that the last update appears to be sufficiently far in the
191 // past to appear that scrolling has stopped by now in viewportTimerFired. 214 // past to appear that scrolling has stopped by now in viewportTimerFired.
192 m_lastLocationUpdateTime = monotonicallyIncreasingTime() - kViewportTimerPol lDelay - 1; 215 m_lastLocationUpdateTime = monotonicallyIncreasingTime() - kViewportTimerPol lDelay - 1;
193 viewportTimerFired(nullptr); 216 viewportTimerFired(nullptr);
194 } 217 }
195 218
196 void AutoplayExperimentHelper::viewportTimerFired(Timer<AutoplayExperimentHelper >*) 219 void AutoplayExperimentHelper::viewportTimerFired(Timer<AutoplayExperimentHelper >*)
197 { 220 {
198 double now = monotonicallyIncreasingTime(); 221 double now = monotonicallyIncreasingTime();
199 double delta = now - m_lastLocationUpdateTime; 222 double delta = now - m_lastLocationUpdateTime;
200 if (delta < kViewportTimerPollDelay) { 223 if (delta < kViewportTimerPollDelay) {
201 // If we are not visible, then skip the timer. It will be started 224 // If we are not visible, then skip the timer. It will be started
202 // again if we become visible again. 225 // again if we become visible again.
203 if (m_wasInViewport) 226 if (m_wasInViewport)
204 m_viewportTimer.startOneShot(kViewportTimerPollDelay - delta, BLINK_ FROM_HERE); 227 m_viewportTimer.startOneShot(kViewportTimerPollDelay - delta, BLINK_ FROM_HERE);
205 228
206 return; 229 return;
207 } 230 }
208 231
209 // Sufficient time has passed since the last scroll that we'll 232 // Sufficient time has passed since the last scroll that we'll
210 // treat it as the end of scroll. Autoplay if we should. 233 // treat it as the end of scroll. Autoplay if we should.
211 maybeStartPlaying(); 234 maybeStartPlaying();
212 } 235 }
213 236
214 bool AutoplayExperimentHelper::meetsVisibilityRequirements() const 237 bool AutoplayExperimentHelper::meetsVisibilityRequirements() const
215 { 238 {
216 if (enabled(IfPageVisible) 239 if (enabled(IfPageVisible)
217 && element().document().pageVisibilityState() != PageVisibilityStateVisi ble) 240 && client().pageVisibilityState() != PageVisibilityStateVisible)
218 return false; 241 return false;
219 242
220 if (!enabled(IfViewport)) 243 if (!enabled(IfViewport))
221 return true; 244 return true;
222 245
223 if (m_lastVisibleRect.isEmpty()) 246 if (m_lastVisibleRect.isEmpty())
224 return false; 247 return false;
225 248
226 LayoutObject* layoutObject = element().layoutObject(); 249 IntRect currentLocation = client().absoluteBoundingBoxRect();
227 if (!layoutObject) 250 if (currentLocation.isEmpty())
228 return false; 251 return false;
229 252
230 IntRect currentLocation = layoutObject->absoluteBoundingBoxRect();
231
232 // If element completely fills the screen, then truncate it to exactly 253 // If element completely fills the screen, then truncate it to exactly
233 // match the screen. Any element that is wider just has to cover. 254 // match the screen. Any element that is wider just has to cover.
234 if (currentLocation.x() <= m_lastVisibleRect.x() 255 if (currentLocation.x() <= m_lastVisibleRect.x()
235 && currentLocation.x() + currentLocation.width() >= m_lastVisibleRect.x( ) + m_lastVisibleRect.width()) { 256 && currentLocation.x() + currentLocation.width() >= m_lastVisibleRect.x( ) + m_lastVisibleRect.width()) {
236 currentLocation.setX(m_lastVisibleRect.x()); 257 currentLocation.setX(m_lastVisibleRect.x());
237 currentLocation.setWidth(m_lastVisibleRect.width()); 258 currentLocation.setWidth(m_lastVisibleRect.width());
238 } 259 }
239 260
240 if (currentLocation.y() <= m_lastVisibleRect.y() 261 if (currentLocation.y() <= m_lastVisibleRect.y()
241 && currentLocation.y() + currentLocation.height() >= m_lastVisibleRect.y () + m_lastVisibleRect.height()) { 262 && currentLocation.y() + currentLocation.height() >= m_lastVisibleRect.y () + m_lastVisibleRect.height()) {
242 currentLocation.setY(m_lastVisibleRect.y()); 263 currentLocation.setY(m_lastVisibleRect.y());
243 currentLocation.setHeight(m_lastVisibleRect.height()); 264 currentLocation.setHeight(m_lastVisibleRect.height());
244 } 265 }
245 266
246 return m_lastVisibleRect.contains(currentLocation); 267 return m_lastVisibleRect.contains(currentLocation);
247 } 268 }
248 269
249 bool AutoplayExperimentHelper::maybeStartPlaying() 270 bool AutoplayExperimentHelper::maybeStartPlaying()
250 { 271 {
251 // See if we're allowed to autoplay now. 272 // See if we're allowed to autoplay now.
252 if (!isEligible() || !meetsVisibilityRequirements()) { 273 if (!isEligible() || !meetsVisibilityRequirements()) {
253 return false; 274 return false;
254 } 275 }
255 276
256 // Start playing! 277 // Start playing!
257 prepareToPlay(element().shouldAutoplay() 278 prepareToAutoplay(client().shouldAutoplay()
258 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll 279 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll
259 : GesturelessPlaybackStartedByPlayMethodAfterScroll); 280 : GesturelessPlaybackStartedByPlayMethodAfterScroll);
260 element().playInternal(); 281
282 // Record that this played without a user gesture.
283 // This should rarely actually do anything. Usually, playMethodCalled()
284 // and becameReadyToPlay will handle it, but toggling muted state can,
285 // in some cases, also trigger autoplay if the autoplay attribute is set
286 // after the media is ready to play.
287 autoplayMediaEncountered();
288
289 client().playInternal();
261 290
262 return true; 291 return true;
263 } 292 }
264 293
265 bool AutoplayExperimentHelper::isEligible() const 294 bool AutoplayExperimentHelper::isEligible() const
266 { 295 {
267 if (m_mode == Mode::ExperimentOff) 296 if (m_mode == Mode::ExperimentOff)
268 return false; 297 return false;
269 298
270 // If no user gesture is required, then the experiment doesn't apply. 299 // If no user gesture is required, then the experiment doesn't apply.
271 // This is what prevents us from starting playback more than once. 300 // This is what prevents us from starting playback more than once.
272 // Since this flag is never set to true once it's cleared, it will block 301 // Since this flag is never set to true once it's cleared, it will block
273 // the autoplay experiment forever. 302 // the autoplay experiment forever.
274 if (!element().isUserGestureRequiredForPlay()) 303 if (!isUserGestureRequiredForPlay())
275 return false; 304 return false;
276 305
277 // Make sure that this is an element of the right type. 306 // Make sure that this is an element of the right type.
278 if (!enabled(ForVideo) && isHTMLVideoElement(element())) 307 if (!enabled(ForVideo) && client().isHTMLVideoElement())
279 return false; 308 return false;
280 309
281 if (!enabled(ForAudio) && isHTMLAudioElement(element())) 310 if (!enabled(ForAudio) && client().isHTMLAudioElement())
282 return false; 311 return false;
283 312
284 // If nobody has requested playback, either by the autoplay attribute or 313 // If nobody has requested playback, either by the autoplay attribute or
285 // a play() call, then do nothing. 314 // a play() call, then do nothing.
286 315
287 if (!m_playPending && !element().shouldAutoplay()) 316 if (!m_playPending && !client().shouldAutoplay())
288 return false; 317 return false;
289 318
290 // Note that the viewport test always returns false on desktop, which is 319 // Note that the viewport test always returns false on desktop, which is
291 // why video-autoplay-experiment.html doesn't check -ifmobile . 320 // why video-autoplay-experiment.html doesn't check -ifmobile .
292 if (enabled(IfMobile) 321 if (enabled(IfMobile)
293 && !document().viewportDescription().isLegacyViewportType()) 322 && !client().isLegacyViewportType())
294 return false; 323 return false;
295 324
296 // If we require muted media and this is muted, then it is eligible. 325 // If we require muted media and this is muted, then it is eligible.
297 if (enabled(IfMuted)) 326 if (enabled(IfMuted))
298 return element().muted(); 327 return client().muted();
299 328
300 // Element is eligible for gesture override, maybe muted. 329 // Element is eligible for gesture override, maybe muted.
301 return true; 330 return true;
302 } 331 }
303 332
304 void AutoplayExperimentHelper::muteIfNeeded() 333 void AutoplayExperimentHelper::muteIfNeeded()
305 { 334 {
306 if (enabled(PlayMuted)) { 335 if (enabled(PlayMuted)) {
307 ASSERT(!isEligible()); 336 ASSERT(!isEligible());
308 // If we are actually changing the muted state, then this will call 337 // If we are actually changing the muted state, then this will call
309 // mutedChanged(). If isEligible(), then mutedChanged() will try 338 // mutedChanged(). If isEligible(), then mutedChanged() will try
310 // to start playback, which we should not do here. 339 // to start playback, which we should not do here.
311 element().setMuted(true); 340 client().setMuted(true);
312 } 341 }
313 } 342 }
314 343
315 void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric) 344 void AutoplayExperimentHelper::removeUserGestureRequirement(AutoplayMetrics metr ic)
316 { 345 {
317 element().recordAutoplayMetric(metric); 346 if (client().isUserGestureRequiredForPlay()) {
347 m_autoplayDeferredMetric = metric;
348 client().removeUserGestureRequirement();
349 }
350 }
318 351
352 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric)
353 {
319 // This also causes !isEligible, so that we don't allow autoplay more than 354 // This also causes !isEligible, so that we don't allow autoplay more than
320 // once. Be sure to do this before muteIfNeeded(). 355 // once. Be sure to do this before muteIfNeeded().
321 element().removeUserGestureRequirement(); 356 // Also note that, at this point, we know that we're goint to start
357 // playback. However, we still don't record the metric here. Instead,
358 // we let playbackStarted() do that later.
359 removeUserGestureRequirement(metric);
360
361 // Don't bother to call autoplayMediaEncountered, since whoever initiates
362 // playback has do it anyway, in case we don't allow autoplay.
322 363
323 unregisterForPositionUpdatesIfNeeded(); 364 unregisterForPositionUpdatesIfNeeded();
324 muteIfNeeded(); 365 muteIfNeeded();
325 366
326 // Record that this autoplayed without a user gesture. This is normally
327 // set when we discover an autoplay attribute, but we include all cases
328 // where playback started without a user gesture, e.g., play().
329 element().setInitialPlayWithoutUserGestures(true);
330
331 // Do not actually start playback here. 367 // Do not actually start playback here.
332 } 368 }
333 369
334 Document& AutoplayExperimentHelper::document() const 370 AutoplayExperimentHelper::Client& AutoplayExperimentHelper::client() const
335 { 371 {
336 return element().document(); 372 return m_client;
337 }
338
339 HTMLMediaElement& AutoplayExperimentHelper::element() const
340 {
341 ASSERT(m_element);
342 return *m_element;
343 } 373 }
344 374
345 AutoplayExperimentHelper::Mode AutoplayExperimentHelper::fromString(const String & mode) 375 AutoplayExperimentHelper::Mode AutoplayExperimentHelper::fromString(const String & mode)
346 { 376 {
347 Mode value = ExperimentOff; 377 Mode value = ExperimentOff;
348 if (mode.contains("-forvideo")) 378 if (mode.contains("-forvideo"))
349 value |= ForVideo; 379 value |= ForVideo;
350 if (mode.contains("-foraudio")) 380 if (mode.contains("-foraudio"))
351 value |= ForAudio; 381 value |= ForAudio;
352 if (mode.contains("-ifpagevisible")) 382 if (mode.contains("-ifpagevisible"))
353 value |= IfPageVisible; 383 value |= IfPageVisible;
354 if (mode.contains("-ifviewport")) 384 if (mode.contains("-ifviewport"))
355 value |= IfViewport; 385 value |= IfViewport;
356 if (mode.contains("-ifmuted")) 386 if (mode.contains("-ifmuted"))
357 value |= IfMuted; 387 value |= IfMuted;
358 if (mode.contains("-ifmobile")) 388 if (mode.contains("-ifmobile"))
359 value |= IfMobile; 389 value |= IfMobile;
360 if (mode.contains("-playmuted")) 390 if (mode.contains("-playmuted"))
361 value |= PlayMuted; 391 value |= PlayMuted;
362 392
363 return value; 393 return value;
364 } 394 }
365 395
396 void AutoplayExperimentHelper::autoplayMediaEncountered()
397 {
398 if (!m_autoplayMediaEncountered) {
399 m_autoplayMediaEncountered = true;
400 recordAutoplayMetric(AutoplayMediaFound);
401 }
402 }
403
404 bool AutoplayExperimentHelper::isUserGestureRequiredForPlay() const
405 {
406 return client().isUserGestureRequiredForPlay();
407 }
408
409 void AutoplayExperimentHelper::recordMetricsBeforePause()
410 {
411 ASSERT(!client().paused());
412
413 const bool bailout = isBailout();
414
415 // Record that play was paused. We don't care if it was autoplay,
416 // play(), or the user manually started it.
417 recordAutoplayMetric(AnyPlaybackPaused);
418 if (bailout)
419 recordAutoplayMetric(AnyPlaybackBailout);
420
421 // If this was a gestureless play, then record that separately.
422 // These cover attr and play() gestureless starts.
423 if (m_waitingForAutoplayPlaybackEnd) {
424 m_waitingForAutoplayPlaybackEnd = false;
425
426 recordAutoplayMetric(AutoplayPaused);
427
428 if (bailout)
429 recordAutoplayMetric(AutoplayBailout);
430 }
431 }
432
433 void AutoplayExperimentHelper::playbackStarted()
434 {
435 recordAutoplayMetric(AnyPlaybackStarted);
436
437 if (!m_playbackStartedMetricRecorded) {
sof 2016/02/16 08:40:05 if (m_playbackStartedMetricRecorded) return; .
liberato (no reviews please) 2016/02/24 18:44:05 Done.
438 m_playbackStartedMetricRecorded = true;
439
440 // If this is a gestureless start, record why it was allowed.
441 if (!UserGestureIndicator::processingUserGesture()) {
442 m_waitingForAutoplayPlaybackEnd = true;
443 recordAutoplayMetric(m_autoplayDeferredMetric);
444 }
445 }
446 }
447
448 void AutoplayExperimentHelper::playbackEnded()
449 {
450 recordAutoplayMetric(AnyPlaybackComplete);
451 if (m_waitingForAutoplayPlaybackEnd) {
sof 2016/02/16 08:40:05 if (!m_waitingForAutoplayPlaybackEnd) return;
liberato (no reviews please) 2016/02/24 18:44:05 Done.
452 m_waitingForAutoplayPlaybackEnd = false;
453 recordAutoplayMetric(AutoplayComplete);
454 }
455 }
456
457 void AutoplayExperimentHelper::recordAutoplayMetric(AutoplayMetrics metric)
458 {
459 client().recordAutoplayMetric(metric);
460 }
461
462 bool AutoplayExperimentHelper::isBailout() const
463 {
464 // We count the user as having bailed-out on the video if they watched
465 // less than one minute and less than 50% of it.
466 const double playedTime = client().currentTime();
467 const double progress = playedTime / client().duration();
468 return (playedTime < 60) && (progress < 0.5);
469 }
470
471 void AutoplayExperimentHelper::recordSandboxFailure()
472 {
473 // We record autoplayMediaEncountered here because we know
474 // that the autoplay attempt will fail.
475 autoplayMediaEncountered();
476 recordAutoplayMetric(AutoplayDisabledBySandbox);
477 }
478
479 void AutoplayExperimentHelper::loadingStarted()
480 {
481 if (m_recordedElement)
482 return;
483
484 m_recordedElement = true;
485 recordAutoplayMetric(client().isHTMLVideoElement()
486 ? AnyVideoElement
487 : AnyAudioElement);
488 }
489
366 } // namespace blink 490 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698