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

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: more oilpan. 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 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/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/layout/api/LayoutMediaItem.h"
16 #include "core/page/Page.h" 14 #include "core/page/Page.h"
17 #include "platform/Logging.h" 15 #include "platform/Logging.h"
18 #include "platform/UserGestureIndicator.h" 16 #include "platform/UserGestureIndicator.h"
19 #include "platform/geometry/IntRect.h" 17 #include "platform/geometry/IntRect.h"
20 18
21 namespace blink { 19 namespace blink {
22 20
23 using namespace HTMLNames; 21 using namespace HTMLNames;
24 22
25 // 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.
26 static const double kViewportTimerPollDelay = 0.5; 24 static const double kViewportTimerPollDelay = 0.5;
27 25
28 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element) 26 AutoplayExperimentHelper::AutoplayExperimentHelper(Client* client)
29 : m_element(&element) 27 : m_client(client)
30 , m_mode(Mode::ExperimentOff) 28 , m_mode(Mode::ExperimentOff)
31 , m_playPending(false) 29 , m_playPending(false)
32 , m_registeredWithLayoutObject(false) 30 , m_registeredWithLayoutObject(false)
33 , m_wasInViewport(false) 31 , m_wasInViewport(false)
32 , m_autoplayMediaEncountered(false)
33 , m_playbackStartedMetricRecorded(false)
34 , m_waitingForAutoplayPlaybackEnd(false)
35 , m_recordedElement(false)
34 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity()) 36 , m_lastLocationUpdateTime(-std::numeric_limits<double>::infinity())
35 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired) 37 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired)
38 , m_autoplayDeferredMetric(GesturelessPlaybackNotOverridden)
36 { 39 {
37 if (document().settings()) { 40 m_mode = fromString(this->client().autoplayExperimentMode());
38 m_mode = fromString(document().settings()->autoplayExperimentMode());
39 41
40 if (m_mode != Mode::ExperimentOff) { 42 if (m_mode != Mode::ExperimentOff) {
41 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d", 43 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d",
42 m_mode); 44 m_mode);
43 }
44 } 45 }
45 } 46 }
46 47
47 AutoplayExperimentHelper::~AutoplayExperimentHelper() 48 AutoplayExperimentHelper::~AutoplayExperimentHelper()
48 { 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.
49 unregisterForPositionUpdatesIfNeeded(); 60 unregisterForPositionUpdatesIfNeeded();
50 } 61 }
51 62
52 void AutoplayExperimentHelper::becameReadyToPlay() 63 void AutoplayExperimentHelper::becameReadyToPlay()
53 { 64 {
54 // Assuming that we're eligible to override the user gesture requirement, 65 // Assuming that we're eligible to override the user gesture requirement,
55 // 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
56 // 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
57 if (isEligible()) { 71 if (isEligible()) {
58 if (meetsVisibilityRequirements()) 72 if (meetsVisibilityRequirements())
59 prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately); 73 prepareToAutoplay(GesturelessPlaybackStartedByAutoplayFlagImmediatel y);
60 else 74 else
61 registerForPositionUpdatesIfNeeded(); 75 registerForPositionUpdatesIfNeeded();
62 } 76 }
63 } 77 }
64 78
65 void AutoplayExperimentHelper::playMethodCalled() 79 void AutoplayExperimentHelper::playMethodCalled()
66 { 80 {
67 // 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.
68 // Eligibility can change if, for example, the mute status changes. 82 // Eligibility can change if, for example, the mute status changes.
69 // Having this set is okay. 83 // Having this set is okay.
70 m_playPending = true; 84 m_playPending = true;
71 85
72 if (!UserGestureIndicator::processingUserGesture()) { 86 if (!UserGestureIndicator::processingUserGesture()) {
87 autoplayMediaEncountered();
73 88
74 if (isEligible()) { 89 if (isEligible()) {
75 // Remember that userGestureRequiredForPlay is required for 90 // Remember that userGestureRequiredForPlay is required for
76 // us to be eligible for the experiment. 91 // us to be eligible for the experiment.
77 // If we are able to override the gesture requirement now, then 92 // If we are able to override the gesture requirement now, then
78 // do so. Otherwise, install an event listener if we need one. 93 // do so. Otherwise, install an event listener if we need one.
79 if (meetsVisibilityRequirements()) { 94 if (meetsVisibilityRequirements()) {
80 // Override the gesture and play. 95 // Override the gesture and assume that play() will succeed.
81 prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately) ; 96 prepareToAutoplay(GesturelessPlaybackStartedByPlayMethodImmediat ely);
82 } else { 97 } else {
83 // Wait for viewport visibility. 98 // Wait for viewport visibility.
84 registerForPositionUpdatesIfNeeded(); 99 registerForPositionUpdatesIfNeeded();
85 } 100 }
86 } 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;
87 110
88 } else if (element().isUserGestureRequiredForPlay()) {
89 unregisterForPositionUpdatesIfNeeded(); 111 unregisterForPositionUpdatesIfNeeded();
90 } 112 }
91 } 113 }
92 114
93 void AutoplayExperimentHelper::pauseMethodCalled() 115 void AutoplayExperimentHelper::pauseMethodCalled()
94 { 116 {
95 // Don't try to autoplay, if we would have. 117 // Don't try to autoplay, if we would have.
96 m_playPending = false; 118 m_playPending = false;
97 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 }
98 } 134 }
99 135
100 void AutoplayExperimentHelper::mutedChanged() 136 void AutoplayExperimentHelper::mutedChanged()
101 { 137 {
102 // 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
103 // 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
104 // playing, then start playing. In other words, start playing if 140 // playing, then start playing. In other words, start playing if
105 // 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
106 if (!isEligible()) { 149 if (!isEligible()) {
107 unregisterForPositionUpdatesIfNeeded(); 150 unregisterForPositionUpdatesIfNeeded();
108 } else { 151 } else {
109 // Try to play. If we can't, then install a listener. 152 // Try to play. If we can't, then install a listener.
110 if (!maybeStartPlaying()) 153 if (!maybeStartPlaying())
111 registerForPositionUpdatesIfNeeded(); 154 registerForPositionUpdatesIfNeeded();
112 } 155 }
113 } 156 }
114 157
115 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded() 158 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded()
116 { 159 {
117 // 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
118 // need the listener. 161 // need the listener.
119 if (!enabled(IfViewport)) { 162 if (!enabled(IfViewport)) {
120 if (!enabled(IfPageVisible)) 163 if (!enabled(IfPageVisible))
121 return; 164 return;
122 } 165 }
123 166
124 if (LayoutObject* layoutObject = element().layoutObject()) { 167 m_client->setRequestPositionUpdates(true);
125 LayoutMediaItem layoutMediaItem = LayoutMediaItem(toLayoutMedia(layoutOb ject));
126 layoutMediaItem.setRequestPositionUpdates(true);
127 }
128 168
129 // Set this unconditionally, in case we have no layout object yet. 169 // Set this unconditionally, in case we have no layout object yet.
130 m_registeredWithLayoutObject = true; 170 m_registeredWithLayoutObject = true;
131 } 171 }
132 172
133 void AutoplayExperimentHelper::unregisterForPositionUpdatesIfNeeded() 173 void AutoplayExperimentHelper::unregisterForPositionUpdatesIfNeeded()
134 { 174 {
135 if (m_registeredWithLayoutObject) { 175 if (m_registeredWithLayoutObject)
136 if (LayoutObject* obj = element().layoutObject()) { 176 m_client->setRequestPositionUpdates(false);
137 LayoutMediaItem layoutMediaItem = LayoutMediaItem(toLayoutMedia(obj) );
138 layoutMediaItem.setRequestPositionUpdates(false);
139 }
140 }
141 177
142 // 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
143 // have a LayoutObject now, but get one later. 179 // have a LayoutObject now, but get one later.
144 m_registeredWithLayoutObject = false; 180 m_registeredWithLayoutObject = false;
145 } 181 }
146 182
147 void AutoplayExperimentHelper::positionChanged(const IntRect& visibleRect) 183 void AutoplayExperimentHelper::positionChanged(const IntRect& visibleRect)
148 { 184 {
149 // Something, maybe position, has changed. If applicable, start a 185 // Something, maybe position, has changed. If applicable, start a
150 // timer to look for the end of a scroll operation. 186 // timer to look for the end of a scroll operation.
151 // Don't do much work here. 187 // Don't do much work here.
152 // Also note that we are called quite often, including when the 188 // Also note that we are called quite often, including when the
153 // 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
154 // for page visibility changes explicitly. 190 // for page visibility changes explicitly.
191 if (visibleRect.isEmpty())
192 return;
155 193
156 m_lastVisibleRect = visibleRect; 194 m_lastVisibleRect = visibleRect;
157 195
158 if (!element().layoutObject()) 196 IntRect currentLocation = client().absoluteBoundingBoxRect();
197 if (currentLocation.isEmpty())
159 return; 198 return;
160 199
161 IntRect currentLocation = element().layoutObject()->absoluteBoundingBoxRect( );
162 bool inViewport = meetsVisibilityRequirements(); 200 bool inViewport = meetsVisibilityRequirements();
163 201
164 if (m_lastLocation != currentLocation) { 202 if (m_lastLocation != currentLocation) {
165 m_lastLocationUpdateTime = monotonicallyIncreasingTime(); 203 m_lastLocationUpdateTime = monotonicallyIncreasingTime();
166 m_lastLocation = currentLocation; 204 m_lastLocation = currentLocation;
167 } 205 }
168 206
169 if (inViewport && !m_wasInViewport) { 207 if (inViewport && !m_wasInViewport) {
170 // Only reset the timer when we transition from not visible to 208 // Only reset the timer when we transition from not visible to
171 // visible, because resetting the timer isn't cheap. 209 // visible, because resetting the timer isn't cheap.
172 m_viewportTimer.startOneShot(kViewportTimerPollDelay, BLINK_FROM_HERE); 210 m_viewportTimer.startOneShot(kViewportTimerPollDelay, BLINK_FROM_HERE);
173 } 211 }
174 m_wasInViewport = inViewport; 212 m_wasInViewport = inViewport;
175 } 213 }
176 214
177 void AutoplayExperimentHelper::updatePositionNotificationRegistration() 215 void AutoplayExperimentHelper::updatePositionNotificationRegistration()
178 { 216 {
179 if (m_registeredWithLayoutObject) { 217 if (m_registeredWithLayoutObject)
180 LayoutMediaItem layoutMediaItem = LayoutMediaItem(toLayoutMedia(element( ).layoutObject())); 218 m_client->setRequestPositionUpdates(true);
181 layoutMediaItem.setRequestPositionUpdates(true);
182 }
183 } 219 }
184 220
185 void AutoplayExperimentHelper::triggerAutoplayViewportCheckForTesting() 221 void AutoplayExperimentHelper::triggerAutoplayViewportCheckForTesting()
186 { 222 {
187 FrameView* view = document().view();
188 if (view)
189 positionChanged(view->rootFrameToContents(view->computeVisibleArea()));
190
191 // 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
192 // past to appear that scrolling has stopped by now in viewportTimerFired. 224 // past to appear that scrolling has stopped by now in viewportTimerFired.
193 m_lastLocationUpdateTime = monotonicallyIncreasingTime() - kViewportTimerPol lDelay - 1; 225 m_lastLocationUpdateTime = monotonicallyIncreasingTime() - kViewportTimerPol lDelay - 1;
194 viewportTimerFired(nullptr); 226 viewportTimerFired(nullptr);
195 } 227 }
196 228
197 void AutoplayExperimentHelper::viewportTimerFired(Timer<AutoplayExperimentHelper >*) 229 void AutoplayExperimentHelper::viewportTimerFired(Timer<AutoplayExperimentHelper >*)
198 { 230 {
199 double now = monotonicallyIncreasingTime(); 231 double now = monotonicallyIncreasingTime();
200 double delta = now - m_lastLocationUpdateTime; 232 double delta = now - m_lastLocationUpdateTime;
201 if (delta < kViewportTimerPollDelay) { 233 if (delta < kViewportTimerPollDelay) {
202 // 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
203 // again if we become visible again. 235 // again if we become visible again.
204 if (m_wasInViewport) 236 if (m_wasInViewport)
205 m_viewportTimer.startOneShot(kViewportTimerPollDelay - delta, BLINK_ FROM_HERE); 237 m_viewportTimer.startOneShot(kViewportTimerPollDelay - delta, BLINK_ FROM_HERE);
206 238
207 return; 239 return;
208 } 240 }
209 241
210 // Sufficient time has passed since the last scroll that we'll 242 // Sufficient time has passed since the last scroll that we'll
211 // treat it as the end of scroll. Autoplay if we should. 243 // treat it as the end of scroll. Autoplay if we should.
212 maybeStartPlaying(); 244 maybeStartPlaying();
213 } 245 }
214 246
215 bool AutoplayExperimentHelper::meetsVisibilityRequirements() const 247 bool AutoplayExperimentHelper::meetsVisibilityRequirements() const
216 { 248 {
217 if (enabled(IfPageVisible) 249 if (enabled(IfPageVisible)
218 && element().document().pageVisibilityState() != PageVisibilityStateVisi ble) 250 && client().pageVisibilityState() != PageVisibilityStateVisible)
219 return false; 251 return false;
220 252
221 if (!enabled(IfViewport)) 253 if (!enabled(IfViewport))
222 return true; 254 return true;
223 255
224 if (m_lastVisibleRect.isEmpty()) 256 if (m_lastVisibleRect.isEmpty())
225 return false; 257 return false;
226 258
227 LayoutObject* layoutObject = element().layoutObject(); 259 IntRect currentLocation = client().absoluteBoundingBoxRect();
228 if (!layoutObject) 260 if (currentLocation.isEmpty())
229 return false; 261 return false;
230 262
231 IntRect currentLocation = layoutObject->absoluteBoundingBoxRect();
232
233 // If element completely fills the screen, then truncate it to exactly 263 // If element completely fills the screen, then truncate it to exactly
234 // 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.
235 if (currentLocation.x() <= m_lastVisibleRect.x() 265 if (currentLocation.x() <= m_lastVisibleRect.x()
236 && currentLocation.x() + currentLocation.width() >= m_lastVisibleRect.x( ) + m_lastVisibleRect.width()) { 266 && currentLocation.x() + currentLocation.width() >= m_lastVisibleRect.x( ) + m_lastVisibleRect.width()) {
237 currentLocation.setX(m_lastVisibleRect.x()); 267 currentLocation.setX(m_lastVisibleRect.x());
238 currentLocation.setWidth(m_lastVisibleRect.width()); 268 currentLocation.setWidth(m_lastVisibleRect.width());
239 } 269 }
240 270
241 if (currentLocation.y() <= m_lastVisibleRect.y() 271 if (currentLocation.y() <= m_lastVisibleRect.y()
242 && currentLocation.y() + currentLocation.height() >= m_lastVisibleRect.y () + m_lastVisibleRect.height()) { 272 && currentLocation.y() + currentLocation.height() >= m_lastVisibleRect.y () + m_lastVisibleRect.height()) {
243 currentLocation.setY(m_lastVisibleRect.y()); 273 currentLocation.setY(m_lastVisibleRect.y());
244 currentLocation.setHeight(m_lastVisibleRect.height()); 274 currentLocation.setHeight(m_lastVisibleRect.height());
245 } 275 }
246 276
247 return m_lastVisibleRect.contains(currentLocation); 277 return m_lastVisibleRect.contains(currentLocation);
248 } 278 }
249 279
250 bool AutoplayExperimentHelper::maybeStartPlaying() 280 bool AutoplayExperimentHelper::maybeStartPlaying()
251 { 281 {
252 // See if we're allowed to autoplay now. 282 // See if we're allowed to autoplay now.
253 if (!isEligible() || !meetsVisibilityRequirements()) { 283 if (!isEligible() || !meetsVisibilityRequirements()) {
254 return false; 284 return false;
255 } 285 }
256 286
257 // Start playing! 287 // Start playing!
258 prepareToPlay(element().shouldAutoplay() 288 prepareToAutoplay(client().shouldAutoplay()
259 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll 289 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll
260 : GesturelessPlaybackStartedByPlayMethodAfterScroll); 290 : GesturelessPlaybackStartedByPlayMethodAfterScroll);
261 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();
262 300
263 return true; 301 return true;
264 } 302 }
265 303
266 bool AutoplayExperimentHelper::isEligible() const 304 bool AutoplayExperimentHelper::isEligible() const
267 { 305 {
268 if (m_mode == Mode::ExperimentOff) 306 if (m_mode == Mode::ExperimentOff)
269 return false; 307 return false;
270 308
271 // 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.
272 // This is what prevents us from starting playback more than once. 310 // 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 311 // Since this flag is never set to true once it's cleared, it will block
274 // the autoplay experiment forever. 312 // the autoplay experiment forever.
275 if (!element().isUserGestureRequiredForPlay()) 313 if (!isUserGestureRequiredForPlay())
276 return false; 314 return false;
277 315
278 // Make sure that this is an element of the right type. 316 // Make sure that this is an element of the right type.
279 if (!enabled(ForVideo) && isHTMLVideoElement(element())) 317 if (!enabled(ForVideo) && client().isHTMLVideoElement())
280 return false; 318 return false;
281 319
282 if (!enabled(ForAudio) && isHTMLAudioElement(element())) 320 if (!enabled(ForAudio) && client().isHTMLAudioElement())
283 return false; 321 return false;
284 322
285 // If nobody has requested playback, either by the autoplay attribute or 323 // If nobody has requested playback, either by the autoplay attribute or
286 // a play() call, then do nothing. 324 // a play() call, then do nothing.
287 325
288 if (!m_playPending && !element().shouldAutoplay()) 326 if (!m_playPending && !client().shouldAutoplay())
289 return false; 327 return false;
290 328
291 // 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
292 // why video-autoplay-experiment.html doesn't check -ifmobile . 330 // why video-autoplay-experiment.html doesn't check -ifmobile .
293 if (enabled(IfMobile) 331 if (enabled(IfMobile)
294 && !document().viewportDescription().isLegacyViewportType()) 332 && !client().isLegacyViewportType())
295 return false; 333 return false;
296 334
297 // 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.
298 if (enabled(IfMuted)) 336 if (enabled(IfMuted))
299 return element().muted(); 337 return client().muted();
300 338
301 // Element is eligible for gesture override, maybe muted. 339 // Element is eligible for gesture override, maybe muted.
302 return true; 340 return true;
303 } 341 }
304 342
305 void AutoplayExperimentHelper::muteIfNeeded() 343 void AutoplayExperimentHelper::muteIfNeeded()
306 { 344 {
307 if (enabled(PlayMuted)) { 345 if (enabled(PlayMuted)) {
308 ASSERT(!isEligible()); 346 ASSERT(!isEligible());
309 // 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
310 // mutedChanged(). If isEligible(), then mutedChanged() will try 348 // mutedChanged(). If isEligible(), then mutedChanged() will try
311 // to start playback, which we should not do here. 349 // to start playback, which we should not do here.
312 element().setMuted(true); 350 client().setMuted(true);
313 } 351 }
314 } 352 }
315 353
316 void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric) 354 void AutoplayExperimentHelper::removeUserGestureRequirement(AutoplayMetrics metr ic)
317 { 355 {
318 element().recordAutoplayMetric(metric); 356 if (client().isUserGestureRequiredForPlay()) {
357 m_autoplayDeferredMetric = metric;
358 client().removeUserGestureRequirement();
359 }
360 }
319 361
362 void AutoplayExperimentHelper::prepareToAutoplay(AutoplayMetrics metric)
363 {
320 // 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
321 // once. Be sure to do this before muteIfNeeded(). 365 // once. Be sure to do this before muteIfNeeded().
322 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.
323 373
324 unregisterForPositionUpdatesIfNeeded(); 374 unregisterForPositionUpdatesIfNeeded();
325 muteIfNeeded(); 375 muteIfNeeded();
326 376
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. 377 // Do not actually start playback here.
333 } 378 }
334 379
335 Document& AutoplayExperimentHelper::document() const 380 AutoplayExperimentHelper::Client& AutoplayExperimentHelper::client() const
336 { 381 {
337 return element().document(); 382 return *m_client;
338 }
339
340 HTMLMediaElement& AutoplayExperimentHelper::element() const
341 {
342 ASSERT(m_element);
343 return *m_element;
344 } 383 }
345 384
346 AutoplayExperimentHelper::Mode AutoplayExperimentHelper::fromString(const String & mode) 385 AutoplayExperimentHelper::Mode AutoplayExperimentHelper::fromString(const String & mode)
347 { 386 {
348 Mode value = ExperimentOff; 387 Mode value = ExperimentOff;
349 if (mode.contains("-forvideo")) 388 if (mode.contains("-forvideo"))
350 value |= ForVideo; 389 value |= ForVideo;
351 if (mode.contains("-foraudio")) 390 if (mode.contains("-foraudio"))
352 value |= ForAudio; 391 value |= ForAudio;
353 if (mode.contains("-ifpagevisible")) 392 if (mode.contains("-ifpagevisible"))
354 value |= IfPageVisible; 393 value |= IfPageVisible;
355 if (mode.contains("-ifviewport")) 394 if (mode.contains("-ifviewport"))
356 value |= IfViewport; 395 value |= IfViewport;
357 if (mode.contains("-ifmuted")) 396 if (mode.contains("-ifmuted"))
358 value |= IfMuted; 397 value |= IfMuted;
359 if (mode.contains("-ifmobile")) 398 if (mode.contains("-ifmobile"))
360 value |= IfMobile; 399 value |= IfMobile;
361 if (mode.contains("-playmuted")) 400 if (mode.contains("-playmuted"))
362 value |= PlayMuted; 401 value |= PlayMuted;
363 402
364 return value; 403 return value;
365 } 404 }
366 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
367 } // namespace blink 502 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698