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

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

Powered by Google App Engine
This is Rietveld 408576698