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

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: s/removeUserGestureRequirement/unlockUserGesture 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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 autoplayMediaEncountered(); 64 autoplayMediaEncountered();
65 65
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(bool playing)
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 || playing)
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);
345 }
346
347 void AutoplayExperimentHelper::unlockUserGesture(AutoplayMetrics metric)
348 {
349 // Note that this could be moved back into HTMLMediaElement fairly easily.
350 // It's only here so that we can record the reason, and we can hide the
351 // ordering between unlocking and recording from the element this way.
352 if (client().isLockedPendingUserGesture()) {
mlamouri (slow - plz ping) 2016/05/06 15:05:47 if (!client().isLockedPendingUserGesture()) re
liberato (no reviews please) 2016/05/17 21:24:33 Done.
353 setDeferredOverrideReason(metric);
354 client().unlockUserGesture();
354 } 355 }
355 } 356 }
356 357
357 void AutoplayExperimentHelper::removeUserGestureRequirement(AutoplayMetrics metr ic) 358 void AutoplayExperimentHelper::setDeferredOverrideReason(AutoplayMetrics metric)
358 { 359 {
359 if (client().isUserGestureRequiredForPlay()) { 360 // If the player is unlocked, then we don't care about any later reason.
361 if (client().isLockedPendingUserGesture())
mlamouri (slow - plz ping) 2016/05/06 15:05:47 ditto
liberato (no reviews please) 2016/05/17 21:24:32 Done.
360 m_autoplayDeferredMetric = metric; 362 m_autoplayDeferredMetric = metric;
361 client().removeUserGestureRequirement();
362 }
363 } 363 }
364 364
365 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric) 365 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric)
366 { 366 {
367 // This also causes !isEligible, so that we don't allow autoplay more than 367 // This also causes !isEligible, so that we don't allow autoplay more than
368 // once. Be sure to do this before muteIfNeeded(). 368 // once. Be sure to do this before muteIfNeeded().
369 // Also note that, at this point, we know that we're goint to start 369 // 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, 370 // playback. However, we still don't record the metric here. Instead,
371 // we let playbackStarted() do that later. 371 // we let playbackStarted() do that later.
372 removeUserGestureRequirement(metric); 372 setDeferredOverrideReason(metric);
373 373
374 // Don't bother to call autoplayMediaEncountered, since whoever initiates 374 // Don't bother to call autoplayMediaEncountered, since whoever initiates
375 // playback has do it anyway, in case we don't allow autoplay. 375 // playback has do it anyway, in case we don't allow autoplay.
376 376
377 unregisterForPositionUpdatesIfNeeded(); 377 unregisterForPositionUpdatesIfNeeded();
378 muteIfNeeded(); 378 muteIfNeeded();
379 379
380 // Do not actually start playback here. 380 // Do not actually start playback here.
381 } 381 }
382 382
(...skipping 28 matching lines...) Expand all
411 } 411 }
412 412
413 void AutoplayExperimentHelper::autoplayMediaEncountered() 413 void AutoplayExperimentHelper::autoplayMediaEncountered()
414 { 414 {
415 if (!m_autoplayMediaEncountered) { 415 if (!m_autoplayMediaEncountered) {
416 m_autoplayMediaEncountered = true; 416 m_autoplayMediaEncountered = true;
417 recordAutoplayMetric(AutoplayMediaFound); 417 recordAutoplayMetric(AutoplayMediaFound);
418 } 418 }
419 } 419 }
420 420
421 bool AutoplayExperimentHelper::isUserGestureRequiredForPlay() const 421 bool AutoplayExperimentHelper::isLockedPendingUserGesture() const
422 { 422 {
423 return client().isUserGestureRequiredForPlay(); 423 return client().isLockedPendingUserGesture();
424 } 424 }
425 425
426 void AutoplayExperimentHelper::playbackStarted() 426 void AutoplayExperimentHelper::playbackStarted()
427 { 427 {
428 recordAutoplayMetric(AnyPlaybackStarted); 428 recordAutoplayMetric(AnyPlaybackStarted);
429 429
430 // Forget about our most recent visibility check. If another override is
431 // requested, then we'll have to refresh it. That way, we don't need to
432 // keep it up to date in the interim.
433 m_lastVisibleRect = IntRect();
434 m_wasInViewport = false;
435
436 // Any pending play is now playing.
437 m_playPending = false;
438
430 if (m_playbackStartedMetricRecorded) 439 if (m_playbackStartedMetricRecorded)
431 return; 440 return;
432 441
442 // Whether we record anything or not, we only want to record metrics for
443 // the initial playback.
433 m_playbackStartedMetricRecorded = true; 444 m_playbackStartedMetricRecorded = true;
434 445
435 // If this is a gestureless start, record why it was allowed. 446 // If this is a gestureless start, record why it was allowed.
436 if (!UserGestureIndicator::processingUserGesture()) { 447 if (!UserGestureIndicator::processingUserGesture()) {
mlamouri (slow - plz ping) 2016/05/06 15:05:47 Slightly out of topic but if we don't preload, wou
liberato (no reviews please) 2016/05/17 21:24:32 wow, good catch. i'll cache this on play() and ot
437 m_waitingForAutoplayPlaybackEnd = true; 448 m_waitingForAutoplayPlaybackEnd = true;
438 recordAutoplayMetric(m_autoplayDeferredMetric); 449 recordAutoplayMetric(m_autoplayDeferredMetric);
439 } 450 }
440 } 451 }
441 452
442 void AutoplayExperimentHelper::playbackStopped() 453 void AutoplayExperimentHelper::playbackStopped()
443 { 454 {
444 const bool ended = client().ended(); 455 const bool ended = client().ended();
445 const bool bailout = isBailout(); 456 const bool bailout = isBailout();
446 457
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 { 510 {
500 return client().isHTMLVideoElement() && (enabled(IfViewport) || enabled(IfPa rtialViewport)); 511 return client().isHTMLVideoElement() && (enabled(IfViewport) || enabled(IfPa rtialViewport));
501 } 512 }
502 513
503 bool AutoplayExperimentHelper::isExperimentEnabled() 514 bool AutoplayExperimentHelper::isExperimentEnabled()
504 { 515 {
505 return m_mode != Mode::ExperimentOff; 516 return m_mode != Mode::ExperimentOff;
506 } 517 }
507 518
508 } // namespace blink 519 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698