Index: third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp |
diff --git a/third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp b/third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp |
index a06dc57a0eb3315f58a5bc6205000bc07364cb48..5f6a3a15545bf002c20257d65e8d66cb317f5962 100644 |
--- a/third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp |
+++ b/third_party/WebKit/Source/core/html/AutoplayExperimentHelper.cpp |
@@ -31,8 +31,12 @@ AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element) |
, m_playPending(false) |
, m_registeredWithLayoutObject(false) |
, m_wasInViewport(false) |
+ , m_autoplayMediaCounted(false) |
+ , m_initialPlayWithoutUserGesture(false) |
+ , m_recordedElement(false) |
, m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity()) |
, m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired) |
+ , m_autoplayDeferredMetric(GesturelessPlaybackUnknownReason) |
{ |
if (document().settings()) { |
m_mode = fromString(document().settings()->autoplayExperimentMode()); |
@@ -53,13 +57,16 @@ void AutoplayExperimentHelper::becameReadyToPlay() |
{ |
// Assuming that we're eligible to override the user gesture requirement, |
// either play if we meet the visibility checks, or install a listener |
- // to wait for them to pass. |
+ // to wait for them to pass. We do not actually start playback; our |
+ // caller must do that. |
if (isEligible()) { |
if (meetsVisibilityRequirements()) |
- prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately); |
+ prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediately); |
else |
registerForPositionUpdatesIfNeeded(); |
} |
+ |
+ autoplayMediaEncountered(); |
} |
void AutoplayExperimentHelper::playMethodCalled() |
@@ -70,22 +77,29 @@ void AutoplayExperimentHelper::playMethodCalled() |
m_playPending = true; |
if (!UserGestureIndicator::processingUserGesture()) { |
- |
if (isEligible()) { |
// Remember that userGestureRequiredForPlay is required for |
// us to be eligible for the experiment. |
// If we are able to override the gesture requirement now, then |
// do so. Otherwise, install an event listener if we need one. |
if (meetsVisibilityRequirements()) { |
- // Override the gesture and play. |
- prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately); |
+ // Override the gesture and assume that play() will succeed. |
+ prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediately); |
} else { |
// Wait for viewport visibility. |
registerForPositionUpdatesIfNeeded(); |
} |
} |
- } else if (element().isUserGestureRequiredForPlay()) { |
+ // Now that we might have also overridden the gesture requirement, |
+ // record the autoplay attempt. |
+ autoplayMediaEncountered(); |
+ } else if (isUserGestureRequiredForPlay()) { |
+ if (m_autoplayMediaCounted) |
+ recordAutoplayMetric(AutoplayManualStart); |
+ // Don't let future gestureless playbacks affect metrics. |
+ m_autoplayMediaCounted = true; |
+ |
unregisterForPositionUpdatesIfNeeded(); |
} |
} |
@@ -95,6 +109,24 @@ void AutoplayExperimentHelper::pauseMethodCalled() |
// Don't try to autoplay, if we would have. |
m_playPending = false; |
unregisterForPositionUpdatesIfNeeded(); |
+ |
+ if (!element().paused()) |
+ recordMetricsBeforePause(); |
+} |
+ |
+void AutoplayExperimentHelper::loadMethodCalled() |
+{ |
+ if (!element().paused()) |
+ recordMetricsBeforePause(); |
+ |
+ if (UserGestureIndicator::processingUserGesture() && isUserGestureRequiredForPlay()) { |
+ recordAutoplayMetric(AutoplayEnabledThroughLoad); |
+ element().removeUserGestureRequirement(GesturelessPlaybackEnabledByLoad); |
+ // While usergesture-initiated load()s technically count as autoplayed, |
+ // they don't feel like such to the users and hence we don't want to |
+ // count them for the purposes of metrics. |
+ m_autoplayMediaCounted = true; |
+ } |
} |
void AutoplayExperimentHelper::mutedChanged() |
@@ -255,9 +287,13 @@ bool AutoplayExperimentHelper::maybeStartPlaying() |
} |
// Start playing! |
- prepareToPlay(element().shouldAutoplay() |
+ prepareToAutoplay(element().shouldAutoplay() |
? GesturelessPlaybackStartedByAutoplayFlagAfterScroll |
: GesturelessPlaybackStartedByPlayMethodAfterScroll); |
+ |
+ // Record that this played without a user gesture. |
+ autoplayMediaEncountered(); |
+ |
element().playInternal(); |
return true; |
@@ -272,7 +308,7 @@ bool AutoplayExperimentHelper::isEligible() const |
// This is what prevents us from starting playback more than once. |
// Since this flag is never set to true once it's cleared, it will block |
// the autoplay experiment forever. |
- if (!element().isUserGestureRequiredForPlay()) |
+ if (!isUserGestureRequiredForPlay()) |
return false; |
// Make sure that this is an element of the right type. |
@@ -313,22 +349,21 @@ void AutoplayExperimentHelper::muteIfNeeded() |
} |
} |
-void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric) |
+void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric) |
{ |
- element().recordAutoplayMetric(metric); |
- |
// This also causes !isEligible, so that we don't allow autoplay more than |
// once. Be sure to do this before muteIfNeeded(). |
- element().removeUserGestureRequirement(); |
+ // Also note that, at this point, we know that we're goint to start |
+ // playback. However, we still don't record the metric here. Instead, |
+ // we let autoplayMediaEncountered() do that later. |
+ element().removeUserGestureRequirement(metric); |
+ |
+ // Don't bother to call autoplayMediaEncountered, since whoever initiates |
+ // playback has do it anyway, in case we don't allow autoplay. |
unregisterForPositionUpdatesIfNeeded(); |
muteIfNeeded(); |
- // Record that this autoplayed without a user gesture. This is normally |
- // set when we discover an autoplay attribute, but we include all cases |
- // where playback started without a user gesture, e.g., play(). |
- element().setInitialPlayWithoutUserGestures(true); |
- |
// Do not actually start playback here. |
} |
@@ -364,4 +399,104 @@ AutoplayExperimentHelper::Mode AutoplayExperimentHelper::fromString(const String |
return value; |
} |
+void AutoplayExperimentHelper::autoplayMediaEncountered() |
+{ |
+ if (!m_autoplayMediaCounted) { |
+ m_autoplayMediaCounted = true; |
+ recordAutoplayMetric(AutoplayMediaFound); |
+ |
+ // If no user gesture was required, then assume that playback will |
+ // actually start. |
+ if (!isUserGestureRequiredForPlay()) { |
+ m_initialPlayWithoutUserGesture = true; |
+ recordAutoplayMetric(m_autoplayDeferredMetric); |
+ } |
+ } |
+} |
+ |
+void AutoplayExperimentHelper::initialPlayWithUserGesture() |
+{ |
+ m_initialPlayWithoutUserGesture = false; |
+} |
+ |
+bool AutoplayExperimentHelper::isUserGestureRequiredForPlay() const |
+{ |
+ return element().isUserGestureRequiredForPlay(); |
+} |
+ |
+void AutoplayExperimentHelper::recordMetricsBeforePause() |
+{ |
+ ASSERT(!element().paused()); |
+ |
+ const bool bailout = isBailout(); |
+ |
+ // Record that play was paused. We don't care if it was autoplay, |
+ // play(), or the user manually started it. |
+ recordAutoplayMetric(AnyPlaybackPaused); |
+ if (bailout) |
+ recordAutoplayMetric(AnyPlaybackBailout); |
+ |
+ // If this was a gestureless play, then record that separately. |
+ // These cover attr and play() gestureless starts. |
+ if (m_initialPlayWithoutUserGesture) { |
+ m_initialPlayWithoutUserGesture = false; |
+ |
+ recordAutoplayMetric(AutoplayPaused); |
+ |
+ if (bailout) |
+ recordAutoplayMetric(AutoplayBailout); |
+ } |
+} |
+ |
+void AutoplayExperimentHelper::playbackStarted() |
+{ |
+ recordAutoplayMetric(AnyPlaybackStarted); |
+} |
+ |
+void AutoplayExperimentHelper::playbackEnded() |
+{ |
+ recordAutoplayMetric(AnyPlaybackComplete); |
+ if (m_initialPlayWithoutUserGesture) { |
+ m_initialPlayWithoutUserGesture = false; |
+ recordAutoplayMetric(AutoplayComplete); |
+ } |
+} |
+ |
+void AutoplayExperimentHelper::recordAutoplayMetric(AutoplayMetrics metric) |
+{ |
+ element().recordAutoplayMetric(metric); |
+} |
+ |
+bool AutoplayExperimentHelper::isBailout() const |
+{ |
+ // We count the user as having bailed-out on the video if they watched |
+ // less than one minute and less than 50% of it. |
+ const double playedTime = element().currentTime(); |
+ const double progress = playedTime / element().duration(); |
+ return (playedTime < 60) && (progress < 0.5); |
+} |
+ |
+void AutoplayExperimentHelper::recordSandboxFailure() |
+{ |
+ // We record autoplayMediaEncountered here because we know |
+ // that the autoplay attempt will fail. |
+ autoplayMediaEncountered(); |
+ recordAutoplayMetric(AutoplayDisabledBySandbox); |
+} |
+ |
+void AutoplayExperimentHelper::loadingStarted() |
+{ |
+ if (m_recordedElement) |
+ return; |
+ |
+ m_recordedElement = true; |
+ recordAutoplayMetric(element().isHTMLVideoElement() |
+ ? AnyVideoElement : AnyAudioElement); |
+} |
+ |
+void AutoplayExperimentHelper::setGestureRemovalReason(AutoplayMetrics deferredMetric) |
+{ |
+ m_autoplayDeferredMetric = deferredMetric; |
+} |
+ |
} |