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

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

Powered by Google App Engine
This is Rietveld 408576698