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

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: cl feedback Created 5 years 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 "config.h" 5 #include "config.h"
6 #include "core/html/AutoplayExperimentHelper.h" 6 #include "core/html/AutoplayExperimentHelper.h"
7 7
8 #include "core/dom/Document.h" 8 #include "core/dom/Document.h"
9 #include "core/frame/FrameView.h" 9 #include "core/frame/FrameView.h"
10 #include "core/frame/Settings.h" 10 #include "core/frame/Settings.h"
(...skipping 13 matching lines...) Expand all
24 24
25 // Seconds to wait after a video has stopped moving before playing it. 25 // Seconds to wait after a video has stopped moving before playing it.
26 static const double kViewportTimerPollDelay = 0.5; 26 static const double kViewportTimerPollDelay = 0.5;
27 27
28 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element) 28 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element)
29 : m_element(&element) 29 : m_element(&element)
30 , m_mode(Mode::ExperimentOff) 30 , m_mode(Mode::ExperimentOff)
31 , m_playPending(false) 31 , m_playPending(false)
32 , m_registeredWithLayoutObject(false) 32 , m_registeredWithLayoutObject(false)
33 , m_wasInViewport(false) 33 , m_wasInViewport(false)
34 , m_autoplayMediaCounted(false)
35 , m_initialPlayWithoutUserGesture(false)
36 , m_recordedElement(false)
34 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity()) 37 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity())
35 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired) 38 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired)
39 , m_autoplayDeferredMetric(GesturelessPlaybackUnknownReason)
36 { 40 {
37 if (document().settings()) { 41 if (document().settings()) {
38 m_mode = fromString(document().settings()->autoplayExperimentMode()); 42 m_mode = fromString(document().settings()->autoplayExperimentMode());
39 43
40 if (m_mode != Mode::ExperimentOff) { 44 if (m_mode != Mode::ExperimentOff) {
41 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d", 45 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d",
42 m_mode); 46 m_mode);
43 } 47 }
44 } 48 }
45 } 49 }
46 50
47 AutoplayExperimentHelper::~AutoplayExperimentHelper() 51 AutoplayExperimentHelper::~AutoplayExperimentHelper()
48 { 52 {
49 unregisterForPositionUpdatesIfNeeded(); 53 unregisterForPositionUpdatesIfNeeded();
50 } 54 }
51 55
52 void AutoplayExperimentHelper::becameReadyToPlay() 56 void AutoplayExperimentHelper::becameReadyToPlay()
53 { 57 {
54 // Assuming that we're eligible to override the user gesture requirement, 58 // Assuming that we're eligible to override the user gesture requirement,
55 // either play if we meet the visibility checks, or install a listener 59 // either play if we meet the visibility checks, or install a listener
56 // to wait for them to pass. 60 // to wait for them to pass. We do not actually start playback; our
61 // caller must do that.
57 if (isEligible()) { 62 if (isEligible()) {
58 if (meetsVisibilityRequirements()) 63 if (meetsVisibilityRequirements())
59 prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately); 64 prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediatel y);
60 else 65 else
61 registerForPositionUpdatesIfNeeded(); 66 registerForPositionUpdatesIfNeeded();
62 } 67 }
68
69 autoplayMediaEncountered();
63 } 70 }
64 71
65 void AutoplayExperimentHelper::playMethodCalled() 72 void AutoplayExperimentHelper::playMethodCalled()
66 { 73 {
67 // Set the pending state, even if the play isn't going to be pending. 74 // Set the pending state, even if the play isn't going to be pending.
68 // Eligibility can change if, for example, the mute status changes. 75 // Eligibility can change if, for example, the mute status changes.
69 // Having this set is okay. 76 // Having this set is okay.
70 m_playPending = true; 77 m_playPending = true;
71 78
72 if (!UserGestureIndicator::processingUserGesture()) { 79 if (!UserGestureIndicator::processingUserGesture()) {
73
74 if (isEligible()) { 80 if (isEligible()) {
75 // Remember that userGestureRequiredForPlay is required for 81 // Remember that userGestureRequiredForPlay is required for
76 // us to be eligible for the experiment. 82 // us to be eligible for the experiment.
77 // If we are able to override the gesture requirement now, then 83 // If we are able to override the gesture requirement now, then
78 // do so. Otherwise, install an event listener if we need one. 84 // do so. Otherwise, install an event listener if we need one.
79 if (meetsVisibilityRequirements()) { 85 if (meetsVisibilityRequirements()) {
80 // Override the gesture and play. 86 // Override the gesture and assume that play() will succeed.
81 prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately) ; 87 prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediat ely);
82 } else { 88 } else {
83 // Wait for viewport visibility. 89 // Wait for viewport visibility.
84 registerForPositionUpdatesIfNeeded(); 90 registerForPositionUpdatesIfNeeded();
85 } 91 }
86 } 92 }
87 93
88 } else if (element().isUserGestureRequiredForPlay()) { 94 // Now that we might have also overridden the gesture requirement,
95 // record the autoplay attempt.
96 autoplayMediaEncountered();
97 } else if (isUserGestureRequiredForPlay()) {
98 if (m_autoplayMediaCounted)
99 recordAutoplayMetric(AutoplayManualStart);
100 // Don't let future gestureless playbacks affect metrics.
101 m_autoplayMediaCounted = true;
102
89 unregisterForPositionUpdatesIfNeeded(); 103 unregisterForPositionUpdatesIfNeeded();
90 } 104 }
91 } 105 }
92 106
93 void AutoplayExperimentHelper::pauseMethodCalled() 107 void AutoplayExperimentHelper::pauseMethodCalled()
94 { 108 {
95 // Don't try to autoplay, if we would have. 109 // Don't try to autoplay, if we would have.
96 m_playPending = false; 110 m_playPending = false;
97 unregisterForPositionUpdatesIfNeeded(); 111 unregisterForPositionUpdatesIfNeeded();
112
113 if (!element().paused())
114 recordMetricsBeforePause();
115 }
116
117 void AutoplayExperimentHelper::loadMethodCalled()
118 {
119 if (!element().paused())
120 recordMetricsBeforePause();
121
122 if (UserGestureIndicator::processingUserGesture() && isUserGestureRequiredFo rPlay()) {
123 recordAutoplayMetric(AutoplayEnabledThroughLoad);
124 element().removeUserGestureRequirement(GesturelessPlaybackEnabledByLoad) ;
125 // While usergesture-initiated load()s technically count as autoplayed,
126 // they don't feel like such to the users and hence we don't want to
127 // count them for the purposes of metrics.
128 m_autoplayMediaCounted = true;
129 }
98 } 130 }
99 131
100 void AutoplayExperimentHelper::mutedChanged() 132 void AutoplayExperimentHelper::mutedChanged()
101 { 133 {
102 // If we are no longer eligible for the autoplay experiment, then also 134 // If we are no longer eligible for the autoplay experiment, then also
103 // quit listening for events. If we are eligible, and if we should be 135 // quit listening for events. If we are eligible, and if we should be
104 // playing, then start playing. In other words, start playing if 136 // playing, then start playing. In other words, start playing if
105 // we just needed 'mute' to autoplay. 137 // we just needed 'mute' to autoplay.
106 if (!isEligible()) { 138 if (!isEligible()) {
107 unregisterForPositionUpdatesIfNeeded(); 139 unregisterForPositionUpdatesIfNeeded();
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 } 280 }
249 281
250 bool AutoplayExperimentHelper::maybeStartPlaying() 282 bool AutoplayExperimentHelper::maybeStartPlaying()
251 { 283 {
252 // See if we're allowed to autoplay now. 284 // See if we're allowed to autoplay now.
253 if (!isEligible() || !meetsVisibilityRequirements()) { 285 if (!isEligible() || !meetsVisibilityRequirements()) {
254 return false; 286 return false;
255 } 287 }
256 288
257 // Start playing! 289 // Start playing!
258 prepareToPlay(element().shouldAutoplay() 290 prepareToAutoplay(element().shouldAutoplay()
259 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll 291 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll
260 : GesturelessPlaybackStartedByPlayMethodAfterScroll); 292 : GesturelessPlaybackStartedByPlayMethodAfterScroll);
293
294 // Record that this played without a user gesture.
295 autoplayMediaEncountered();
296
261 element().playInternal(); 297 element().playInternal();
262 298
263 return true; 299 return true;
264 } 300 }
265 301
266 bool AutoplayExperimentHelper::isEligible() const 302 bool AutoplayExperimentHelper::isEligible() const
267 { 303 {
268 if (m_mode == Mode::ExperimentOff) 304 if (m_mode == Mode::ExperimentOff)
269 return false; 305 return false;
270 306
271 // If no user gesture is required, then the experiment doesn't apply. 307 // If no user gesture is required, then the experiment doesn't apply.
272 // This is what prevents us from starting playback more than once. 308 // This is what prevents us from starting playback more than once.
273 // Since this flag is never set to true once it's cleared, it will block 309 // Since this flag is never set to true once it's cleared, it will block
274 // the autoplay experiment forever. 310 // the autoplay experiment forever.
275 if (!element().isUserGestureRequiredForPlay()) 311 if (!isUserGestureRequiredForPlay())
276 return false; 312 return false;
277 313
278 // Make sure that this is an element of the right type. 314 // Make sure that this is an element of the right type.
279 if (!enabled(ForVideo) && isHTMLVideoElement(element())) 315 if (!enabled(ForVideo) && isHTMLVideoElement(element()))
280 return false; 316 return false;
281 317
282 if (!enabled(ForAudio) && isHTMLAudioElement(element())) 318 if (!enabled(ForAudio) && isHTMLAudioElement(element()))
283 return false; 319 return false;
284 320
285 // If nobody has requested playback, either by the autoplay attribute or 321 // If nobody has requested playback, either by the autoplay attribute or
(...skipping 20 matching lines...) Expand all
306 { 342 {
307 if (enabled(PlayMuted)) { 343 if (enabled(PlayMuted)) {
308 ASSERT(!isEligible()); 344 ASSERT(!isEligible());
309 // If we are actually changing the muted state, then this will call 345 // If we are actually changing the muted state, then this will call
310 // mutedChanged(). If isEligible(), then mutedChanged() will try 346 // mutedChanged(). If isEligible(), then mutedChanged() will try
311 // to start playback, which we should not do here. 347 // to start playback, which we should not do here.
312 element().setMuted(true); 348 element().setMuted(true);
313 } 349 }
314 } 350 }
315 351
316 void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric) 352 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric)
317 { 353 {
318 element().recordAutoplayMetric(metric);
319
320 // 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
321 // once. Be sure to do this before muteIfNeeded(). 355 // once. Be sure to do this before muteIfNeeded().
322 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 autoplayMediaEncountered() do that later.
359 element().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.
323 363
324 unregisterForPositionUpdatesIfNeeded(); 364 unregisterForPositionUpdatesIfNeeded();
325 muteIfNeeded(); 365 muteIfNeeded();
326 366
327 // Record that this autoplayed without a user gesture. This is normally
328 // set when we discover an autoplay attribute, but we include all cases
329 // where playback started without a user gesture, e.g., play().
330 element().setInitialPlayWithoutUserGestures(true);
331
332 // Do not actually start playback here. 367 // Do not actually start playback here.
333 } 368 }
334 369
335 Document& AutoplayExperimentHelper::document() const 370 Document& AutoplayExperimentHelper::document() const
336 { 371 {
337 return element().document(); 372 return element().document();
338 } 373 }
339 374
340 HTMLMediaElement& AutoplayExperimentHelper::element() const 375 HTMLMediaElement& AutoplayExperimentHelper::element() const
341 { 376 {
(...skipping 15 matching lines...) Expand all
357 if (mode.contains("-ifmuted")) 392 if (mode.contains("-ifmuted"))
358 value |= IfMuted; 393 value |= IfMuted;
359 if (mode.contains("-ifmobile")) 394 if (mode.contains("-ifmobile"))
360 value |= IfMobile; 395 value |= IfMobile;
361 if (mode.contains("-playmuted")) 396 if (mode.contains("-playmuted"))
362 value |= PlayMuted; 397 value |= PlayMuted;
363 398
364 return value; 399 return value;
365 } 400 }
366 401
402 void AutoplayExperimentHelper::autoplayMediaEncountered()
403 {
404 if (!m_autoplayMediaCounted) {
405 m_autoplayMediaCounted = true;
406 recordAutoplayMetric(AutoplayMediaFound);
407
408 // If no user gesture was required, then assume that playback will
409 // actually start.
410 if (!isUserGestureRequiredForPlay()) {
411 m_initialPlayWithoutUserGesture = true;
412 recordAutoplayMetric(m_autoplayDeferredMetric);
413 }
414 }
367 } 415 }
416
417 void AutoplayExperimentHelper::initialPlayWithUserGesture()
418 {
419 m_initialPlayWithoutUserGesture = false;
420 }
421
422 bool AutoplayExperimentHelper::isUserGestureRequiredForPlay() const
423 {
424 return element().isUserGestureRequiredForPlay();
425 }
426
427 void AutoplayExperimentHelper::recordMetricsBeforePause()
428 {
429 ASSERT(!element().paused());
430
431 const bool bailout = isBailout();
432
433 // Record that play was paused. We don't care if it was autoplay,
434 // play(), or the user manually started it.
435 recordAutoplayMetric(AnyPlaybackPaused);
436 if (bailout)
437 recordAutoplayMetric(AnyPlaybackBailout);
438
439 // If this was a gestureless play, then record that separately.
440 // These cover attr and play() gestureless starts.
441 if (m_initialPlayWithoutUserGesture) {
442 m_initialPlayWithoutUserGesture = false;
443
444 recordAutoplayMetric(AutoplayPaused);
445
446 if (bailout)
447 recordAutoplayMetric(AutoplayBailout);
448 }
449 }
450
451 void AutoplayExperimentHelper::playbackStarted()
452 {
453 recordAutoplayMetric(AnyPlaybackStarted);
454 }
455
456 void AutoplayExperimentHelper::playbackEnded()
457 {
458 recordAutoplayMetric(AnyPlaybackComplete);
459 if (m_initialPlayWithoutUserGesture) {
460 m_initialPlayWithoutUserGesture = false;
461 recordAutoplayMetric(AutoplayComplete);
462 }
463 }
464
465 void AutoplayExperimentHelper::recordAutoplayMetric(AutoplayMetrics metric)
466 {
467 element().recordAutoplayMetric(metric);
468 }
469
470 bool AutoplayExperimentHelper::isBailout() const
471 {
472 // We count the user as having bailed-out on the video if they watched
473 // less than one minute and less than 50% of it.
474 const double playedTime = element().currentTime();
475 const double progress = playedTime / element().duration();
476 return (playedTime < 60) && (progress < 0.5);
477 }
478
479 void AutoplayExperimentHelper::recordSandboxFailure()
480 {
481 // We record autoplayMediaEncountered here because we know
482 // that the autoplay attempt will fail.
483 autoplayMediaEncountered();
484 recordAutoplayMetric(AutoplayDisabledBySandbox);
485 }
486
487 void AutoplayExperimentHelper::loadingStarted()
488 {
489 if (m_recordedElement)
490 return;
491
492 m_recordedElement = true;
493 recordAutoplayMetric(element().isHTMLVideoElement()
494 ? AnyVideoElement : AnyAudioElement);
495 }
496
497 void AutoplayExperimentHelper::setGestureRemovalReason(AutoplayMetrics deferredM etric)
498 {
499 m_autoplayDeferredMetric = deferredMetric;
500 }
501
502 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698