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

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

Issue 1949633002: Don't remove the gesture requirement in the autoplay experiment. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed test expectations. Created 4 years, 7 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/Settings.h" 8 #include "core/frame/Settings.h"
9 #include "core/html/HTMLMediaElement.h" 9 #include "core/html/HTMLMediaElement.h"
10 #include "core/layout/LayoutBox.h" 10 #include "core/layout/LayoutBox.h"
(...skipping 13 matching lines...) Expand all
24 static const double kViewportTimerPollDelay = 0.5; 24 static const double kViewportTimerPollDelay = 0.5;
25 25
26 AutoplayExperimentHelper::AutoplayExperimentHelper(Client* client) 26 AutoplayExperimentHelper::AutoplayExperimentHelper(Client* client)
27 : m_client(client) 27 : m_client(client)
28 , m_mode(Mode::ExperimentOff) 28 , m_mode(Mode::ExperimentOff)
29 , m_playPending(false) 29 , m_playPending(false)
30 , m_registeredWithLayoutObject(false) 30 , m_registeredWithLayoutObject(false)
31 , m_wasInViewport(false) 31 , m_wasInViewport(false)
32 , m_autoplayMediaEncountered(false) 32 , m_autoplayMediaEncountered(false)
33 , m_playbackStartedMetricRecorded(false) 33 , m_playbackStartedMetricRecorded(false)
34 , m_waitingForAutoplayPlaybackEnd(false) 34 , m_waitingForAutoplayPlaybackStop(false)
35 , m_recordedElement(false) 35 , m_recordedElement(false)
36 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity()) 36 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity())
37 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired) 37 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired)
38 , m_autoplayDeferredMetric(GesturelessPlaybackNotOverridden) 38 , m_autoplayDeferredMetric(GesturelessPlaybackNotOverridden)
39 { 39 {
40 m_mode = fromString(this->client().autoplayExperimentMode()); 40 m_mode = fromString(this->client().autoplayExperimentMode());
41 41
42 if (isExperimentEnabled()) { 42 if (isExperimentEnabled()) {
43 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d", 43 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d",
44 m_mode); 44 m_mode);
(...skipping 21 matching lines...) Expand all
66 if (isEligible()) { 66 if (isEligible()) {
67 if (meetsVisibilityRequirements()) 67 if (meetsVisibilityRequirements())
68 prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediatel y); 68 prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediatel y);
69 else 69 else
70 registerForPositionUpdatesIfNeeded(); 70 registerForPositionUpdatesIfNeeded();
71 } 71 }
72 } 72 }
73 73
74 void AutoplayExperimentHelper::playMethodCalled() 74 void AutoplayExperimentHelper::playMethodCalled()
75 { 75 {
76 // Set the pending state, even if the play isn't going to be pending. 76 // If a play is already pending, then do nothing. We're already trying
77 // Eligibility can change if, for example, the mute status changes. 77 // to play. Similarly, do nothing if we're already playing.
78 // Having this set is okay. 78 if (m_playPending || !m_client->paused())
79 m_playPending = true; 79 return;
80 80
81 if (!UserGestureIndicator::utilizeUserGesture()) { 81 if (!UserGestureIndicator::utilizeUserGesture()) {
82 autoplayMediaEncountered(); 82 autoplayMediaEncountered();
83 83
84 if (isEligible()) { 84 // Check for eligibility, but don't worry if playback is currently
85 // Remember that userGestureRequiredForPlay is required for 85 // pending. If we're still not eligible, then this play() will fail.
86 // us to be eligible for the experiment. 86 if (isEligible(IgnorePendingPlayback)) {
87 m_playPending = true;
88
87 // If we are able to override the gesture requirement now, then 89 // If we are able to override the gesture requirement now, then
88 // do so. Otherwise, install an event listener if we need one. 90 // do so. Otherwise, install an event listener if we need one.
91 // We do not actually start playback; play() will do that.
89 if (meetsVisibilityRequirements()) { 92 if (meetsVisibilityRequirements()) {
90 // Override the gesture and assume that play() will succeed. 93 // Override the gesture and assume that play() will succeed.
91 prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediat ely); 94 prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediat ely);
92 } else { 95 } else {
93 // Wait for viewport visibility. 96 // Wait for viewport visibility.
97 // TODO(liberato): if the autoplay is allowed soon enough, then
98 // it should still record *Immediately. Otherwise, we end up
99 // here before the first layout sometimes, when the item is
100 // visible but we just don't know that yet.
94 registerForPositionUpdatesIfNeeded(); 101 registerForPositionUpdatesIfNeeded();
95 } 102 }
96 } 103 }
97 } else if (isUserGestureRequiredForPlay()) { 104 } else if (isLockedPendingUserGesture()) {
98 // If this media tried to autoplay, and we haven't played it yet, then 105 // If this media tried to autoplay, and we haven't played it yet, then
99 // record that the user provided the gesture to start it the first time. 106 // record that the user provided the gesture to start it the first time.
100 if (m_autoplayMediaEncountered && !m_playbackStartedMetricRecorded) 107 if (m_autoplayMediaEncountered && !m_playbackStartedMetricRecorded)
101 recordAutoplayMetric(AutoplayManualStart); 108 recordAutoplayMetric(AutoplayManualStart);
102 // Don't let future gestureless playbacks affect metrics. 109 // Don't let future gestureless playbacks affect metrics.
103 m_autoplayMediaEncountered = true; 110 m_autoplayMediaEncountered = true;
104 m_playbackStartedMetricRecorded = true; 111 m_playbackStartedMetricRecorded = true;
112 m_playPending = false;
105 113
106 unregisterForPositionUpdatesIfNeeded(); 114 unregisterForPositionUpdatesIfNeeded();
107 } 115 }
108 } 116 }
109 117
110 void AutoplayExperimentHelper::pauseMethodCalled() 118 void AutoplayExperimentHelper::pauseMethodCalled()
111 { 119 {
112 // Don't try to autoplay, if we would have. 120 // Don't try to autoplay, if we would have.
113 m_playPending = false; 121 m_playPending = false;
114 unregisterForPositionUpdatesIfNeeded(); 122 unregisterForPositionUpdatesIfNeeded();
115 } 123 }
116 124
117 void AutoplayExperimentHelper::loadMethodCalled() 125 void AutoplayExperimentHelper::loadMethodCalled()
118 { 126 {
119 if (isUserGestureRequiredForPlay() && UserGestureIndicator::utilizeUserGestu re()) { 127 if (isLockedPendingUserGesture() && UserGestureIndicator::utilizeUserGesture ()) {
120 recordAutoplayMetric(AutoplayEnabledThroughLoad); 128 recordAutoplayMetric(AutoplayEnabledThroughLoad);
121 removeUserGestureRequirement(GesturelessPlaybackEnabledByLoad); 129 unlockUserGesture(GesturelessPlaybackEnabledByLoad);
122 } 130 }
123 } 131 }
124 132
125 void AutoplayExperimentHelper::mutedChanged()
126 {
127 // If we are no longer eligible for the autoplay experiment, then also
128 // quit listening for events. If we are eligible, and if we should be
129 // playing, then start playing. In other words, start playing if
130 // we just needed 'mute' to autoplay.
131
132 // Make sure that autoplay was actually deferred. If, for example, the
133 // autoplay attribute is set after the media is ready to play, then it
134 // would normally have no effect. We don't want to start playing.
135 if (!m_autoplayMediaEncountered)
136 return;
137
138 if (!isEligible()) {
139 unregisterForPositionUpdatesIfNeeded();
140 } else {
141 // Try to play. If we can't, then install a listener.
142 if (!maybeStartPlaying())
143 registerForPositionUpdatesIfNeeded();
144 }
145 }
146
147 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded() 133 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded()
148 { 134 {
149 // If we don't require that the player is in the viewport, then we don't 135 // If we don't require that the player is in the viewport, then we don't
150 // need the listener. 136 // need the listener.
151 if (!requiresViewportVisibility()) { 137 if (!requiresViewportVisibility()) {
152 if (!enabled(IfPageVisible)) 138 if (!enabled(IfPageVisible))
153 return; 139 return;
154 } 140 }
155 141
156 m_client->setRequestPositionUpdates(true); 142 m_client->setRequestPositionUpdates(true);
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 currentLocation.setY(m_lastVisibleRect.y()); 254 currentLocation.setY(m_lastVisibleRect.y());
269 currentLocation.setHeight(m_lastVisibleRect.height()); 255 currentLocation.setHeight(m_lastVisibleRect.height());
270 } 256 }
271 257
272 return m_lastVisibleRect.contains(currentLocation); 258 return m_lastVisibleRect.contains(currentLocation);
273 } 259 }
274 260
275 bool AutoplayExperimentHelper::maybeStartPlaying() 261 bool AutoplayExperimentHelper::maybeStartPlaying()
276 { 262 {
277 // See if we're allowed to autoplay now. 263 // See if we're allowed to autoplay now.
278 if (!isEligible() || !meetsVisibilityRequirements()) { 264 if (!isGestureRequirementOverridden())
279 return false; 265 return false;
280 }
281 266
282 // Start playing! 267 // Start playing!
283 prepareToAutoplay(client().shouldAutoplay() 268 prepareToAutoplay(client().shouldAutoplay()
284 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll 269 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll
285 : GesturelessPlaybackStartedByPlayMethodAfterScroll); 270 : GesturelessPlaybackStartedByPlayMethodAfterScroll);
286 271
287 // Record that this played without a user gesture. 272 // Record that this played without a user gesture.
288 // This should rarely actually do anything. Usually, playMethodCalled() 273 // This should rarely actually do anything. Usually, playMethodCalled()
289 // and becameReadyToPlay will handle it, but toggling muted state can, 274 // and becameReadyToPlay will handle it, but toggling muted state can,
290 // in some cases, also trigger autoplay if the autoplay attribute is set 275 // in some cases, also trigger autoplay if the autoplay attribute is set
291 // after the media is ready to play. 276 // after the media is ready to play.
292 autoplayMediaEncountered(); 277 autoplayMediaEncountered();
293 278
294 client().playInternal(); 279 client().playInternal();
295 280
296 return true; 281 return true;
297 } 282 }
298 283
299 bool AutoplayExperimentHelper::isEligible() const 284 bool AutoplayExperimentHelper::isGestureRequirementOverridden() const
285 {
286 return isEligible() && meetsVisibilityRequirements();
287 }
288
289 bool AutoplayExperimentHelper::isPlaybackDeferred() const
290 {
291 return m_playPending;
292 }
293
294 bool AutoplayExperimentHelper::isEligible(EligibilityMode mode) const
300 { 295 {
301 if (m_mode == Mode::ExperimentOff) 296 if (m_mode == Mode::ExperimentOff)
302 return false; 297 return false;
303 298
304 // If autoplay is disabled, no one is eligible. 299 // If autoplay is disabled, no one is eligible.
305 if (!client().isAutoplayAllowedPerSettings()) 300 if (!client().isAutoplayAllowedPerSettings())
306 return false; 301 return false;
307 302
308 // If no user gesture is required, then the experiment doesn't apply. 303 // If no user gesture is required, then the experiment doesn't apply.
309 // This is what prevents us from starting playback more than once. 304 // This is what prevents us from starting playback more than once.
310 // Since this flag is never set to true once it's cleared, it will block 305 // Since this flag is never set to true once it's cleared, it will block
311 // the autoplay experiment forever. 306 // the autoplay experiment forever.
312 if (!isUserGestureRequiredForPlay()) 307 if (!isLockedPendingUserGesture())
313 return false; 308 return false;
314 309
315 // Make sure that this is an element of the right type. 310 // Make sure that this is an element of the right type.
316 if (!enabled(ForVideo) && client().isHTMLVideoElement()) 311 if (!enabled(ForVideo) && client().isHTMLVideoElement())
317 return false; 312 return false;
318 313
319 if (!enabled(ForAudio) && client().isHTMLAudioElement()) 314 if (!enabled(ForAudio) && client().isHTMLAudioElement())
320 return false; 315 return false;
321 316
322 // If nobody has requested playback, either by the autoplay attribute or 317 // If nobody has requested playback, either by the autoplay attribute or
323 // a play() call, then do nothing. 318 // a play() call, then do nothing.
324 319
325 if (!m_playPending && !client().shouldAutoplay()) 320 if (mode != IgnorePendingPlayback && !m_playPending && !client().shouldAuto play())
326 return false; 321 return false;
327 322
328 // Note that the viewport test always returns false on desktop, which is 323 // Note that the viewport test always returns false on desktop, which is
329 // why video-autoplay-experiment.html doesn't check -ifmobile . 324 // why video-autoplay-experiment.html doesn't check -ifmobile .
330 if (enabled(IfMobile) 325 if (enabled(IfMobile)
331 && !client().isLegacyViewportType()) 326 && !client().isLegacyViewportType())
332 return false; 327 return false;
333 328
334 // If we require same-origin, then check the origin. 329 // If we require same-origin, then check the origin.
335 if (enabled(IfSameOrigin) && client().isCrossOrigin()) 330 if (enabled(IfSameOrigin) && client().isCrossOrigin())
336 return false; 331 return false;
337 332
338 // If we require muted media and this is muted, then it is eligible. 333 // If we require muted media and this is muted, then it is eligible.
339 if (enabled(IfMuted)) 334 if (enabled(IfMuted))
340 return client().muted(); 335 return client().muted();
341 336
342 // Element is eligible for gesture override, maybe muted. 337 // Element is eligible for gesture override, maybe muted.
343 return true; 338 return true;
344 } 339 }
345 340
346 void AutoplayExperimentHelper::muteIfNeeded() 341 void AutoplayExperimentHelper::muteIfNeeded()
347 { 342 {
348 if (enabled(PlayMuted)) { 343 if (enabled(PlayMuted))
349 ASSERT(!isEligible());
350 // If we are actually changing the muted state, then this will call
351 // mutedChanged(). If isEligible(), then mutedChanged() will try
352 // to start playback, which we should not do here.
353 client().setMuted(true); 344 client().setMuted(true);
354 }
355 } 345 }
356 346
357 void AutoplayExperimentHelper::removeUserGestureRequirement(AutoplayMetrics metr ic) 347 void AutoplayExperimentHelper::unlockUserGesture(AutoplayMetrics metric)
358 { 348 {
359 if (client().isUserGestureRequiredForPlay()) { 349 // Note that this could be moved back into HTMLMediaElement fairly easily.
360 m_autoplayDeferredMetric = metric; 350 // It's only here so that we can record the reason, and we can hide the
361 client().removeUserGestureRequirement(); 351 // ordering between unlocking and recording from the element this way.
362 } 352 if (!client().isLockedPendingUserGesture())
353 return;
354
355 setDeferredOverrideReason(metric);
356 client().unlockUserGesture();
357 }
358
359 void AutoplayExperimentHelper::setDeferredOverrideReason(AutoplayMetrics metric)
360 {
361 // If the player is unlocked, then we don't care about any later reason.
362 if (!client().isLockedPendingUserGesture())
363 return;
364
365 m_autoplayDeferredMetric = metric;
363 } 366 }
364 367
365 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric) 368 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric)
366 { 369 {
367 // This also causes !isEligible, so that we don't allow autoplay more than 370 // This also causes !isEligible, so that we don't allow autoplay more than
368 // once. Be sure to do this before muteIfNeeded(). 371 // once. Be sure to do this before muteIfNeeded().
369 // Also note that, at this point, we know that we're goint to start 372 // Also note that, at this point, we know that we're goint to start
370 // playback. However, we still don't record the metric here. Instead, 373 // playback. However, we still don't record the metric here. Instead,
371 // we let playbackStarted() do that later. 374 // we let playbackStarted() do that later.
372 removeUserGestureRequirement(metric); 375 setDeferredOverrideReason(metric);
373 376
374 // Don't bother to call autoplayMediaEncountered, since whoever initiates 377 // Don't bother to call autoplayMediaEncountered, since whoever initiates
375 // playback has do it anyway, in case we don't allow autoplay. 378 // playback has do it anyway, in case we don't allow autoplay.
376 379
377 unregisterForPositionUpdatesIfNeeded(); 380 unregisterForPositionUpdatesIfNeeded();
378 muteIfNeeded(); 381 muteIfNeeded();
379 382
380 // Do not actually start playback here. 383 // Do not actually start playback here.
381 } 384 }
382 385
(...skipping 28 matching lines...) Expand all
411 } 414 }
412 415
413 void AutoplayExperimentHelper::autoplayMediaEncountered() 416 void AutoplayExperimentHelper::autoplayMediaEncountered()
414 { 417 {
415 if (!m_autoplayMediaEncountered) { 418 if (!m_autoplayMediaEncountered) {
416 m_autoplayMediaEncountered = true; 419 m_autoplayMediaEncountered = true;
417 recordAutoplayMetric(AutoplayMediaFound); 420 recordAutoplayMetric(AutoplayMediaFound);
418 } 421 }
419 } 422 }
420 423
421 bool AutoplayExperimentHelper::isUserGestureRequiredForPlay() const 424 bool AutoplayExperimentHelper::isLockedPendingUserGesture() const
422 { 425 {
423 return client().isUserGestureRequiredForPlay(); 426 return client().isLockedPendingUserGesture();
424 } 427 }
425 428
426 void AutoplayExperimentHelper::playbackStarted() 429 void AutoplayExperimentHelper::playbackStarted()
427 { 430 {
428 recordAutoplayMetric(AnyPlaybackStarted); 431 recordAutoplayMetric(AnyPlaybackStarted);
429 432
433 // Forget about our most recent visibility check. If another override is
434 // requested, then we'll have to refresh it. That way, we don't need to
435 // keep it up to date in the interim.
436 m_lastVisibleRect = IntRect();
437 m_wasInViewport = false;
438
439 // Any pending play is now playing.
440 m_playPending = false;
441
430 if (m_playbackStartedMetricRecorded) 442 if (m_playbackStartedMetricRecorded)
431 return; 443 return;
432 444
445 // Whether we record anything or not, we only want to record metrics for
446 // the initial playback.
433 m_playbackStartedMetricRecorded = true; 447 m_playbackStartedMetricRecorded = true;
434 448
435 // If this is a gestureless start, record why it was allowed. 449 // If this is a gestureless start, then record why it was allowed.
436 if (!UserGestureIndicator::processingUserGesture()) { 450 if (m_autoplayMediaEncountered) {
437 m_waitingForAutoplayPlaybackEnd = true; 451 m_waitingForAutoplayPlaybackStop = true;
438 recordAutoplayMetric(m_autoplayDeferredMetric); 452 recordAutoplayMetric(m_autoplayDeferredMetric);
439 } 453 }
440 } 454 }
441 455
442 void AutoplayExperimentHelper::playbackStopped() 456 void AutoplayExperimentHelper::playbackStopped()
443 { 457 {
444 const bool ended = client().ended(); 458 const bool ended = client().ended();
445 const bool bailout = isBailout(); 459 const bool bailout = isBailout();
446 460
447 // Record that play was paused. We don't care if it was autoplay, 461 // Record that play was paused. We don't care if it was autoplay,
448 // play(), or the user manually started it. 462 // play(), or the user manually started it.
449 recordAutoplayMetric(ended ? AnyPlaybackComplete : AnyPlaybackPaused); 463 recordAutoplayMetric(ended ? AnyPlaybackComplete : AnyPlaybackPaused);
450 if (bailout) 464 if (bailout)
451 recordAutoplayMetric(AnyPlaybackBailout); 465 recordAutoplayMetric(AnyPlaybackBailout);
452 466
453 // If this was a gestureless play, then record that separately. 467 // If this was a gestureless play, then record that separately.
454 // These cover attr and play() gestureless starts. 468 // These cover attr and play() gestureless starts.
455 if (m_waitingForAutoplayPlaybackEnd) { 469 if (m_waitingForAutoplayPlaybackStop) {
456 m_waitingForAutoplayPlaybackEnd = false; 470 m_waitingForAutoplayPlaybackStop = false;
457 471
458 recordAutoplayMetric(ended ? AutoplayComplete : AutoplayPaused); 472 recordAutoplayMetric(ended ? AutoplayComplete : AutoplayPaused);
459 473
460 if (bailout) 474 if (bailout)
461 recordAutoplayMetric(AutoplayBailout); 475 recordAutoplayMetric(AutoplayBailout);
462 } 476 }
463 } 477 }
464 478
465 void AutoplayExperimentHelper::recordAutoplayMetric(AutoplayMetrics metric) 479 void AutoplayExperimentHelper::recordAutoplayMetric(AutoplayMetrics metric)
466 { 480 {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 { 513 {
500 return client().isHTMLVideoElement() && (enabled(IfViewport) || enabled(IfPa rtialViewport)); 514 return client().isHTMLVideoElement() && (enabled(IfViewport) || enabled(IfPa rtialViewport));
501 } 515 }
502 516
503 bool AutoplayExperimentHelper::isExperimentEnabled() 517 bool AutoplayExperimentHelper::isExperimentEnabled()
504 { 518 {
505 return m_mode != Mode::ExperimentOff; 519 return m_mode != Mode::ExperimentOff;
506 } 520 }
507 521
508 } // namespace blink 522 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698