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

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

Issue 1329853004: Include viewport visibility checks for autoplay experiment. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@autoplay_step1
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 #include "core/html/AutoplayExperimentHelper.h" 6 #include "core/html/AutoplayExperimentHelper.h"
7 7
8 #include "core/dom/Document.h" 8 #include "core/dom/Document.h"
9 #include "core/frame/Settings.h" 9 #include "core/frame/Settings.h"
10 #include "core/html/HTMLMediaElement.h" 10 #include "core/html/HTMLMediaElement.h"
11 #include "core/layout/LayoutBox.h" 11 #include "core/layout/LayoutBox.h"
12 #include "core/layout/LayoutObject.h" 12 #include "core/layout/LayoutObject.h"
13 #include "core/layout/LayoutVideo.h" 13 #include "core/layout/LayoutVideo.h"
14 #include "core/layout/LayoutView.h" 14 #include "core/layout/LayoutView.h"
15 #include "core/page/Page.h" 15 #include "core/page/Page.h"
16 #include "platform/Logging.h" 16 #include "platform/Logging.h"
17 #include "platform/UserGestureIndicator.h" 17 #include "platform/UserGestureIndicator.h"
18 #include "platform/geometry/IntRect.h" 18 #include "platform/geometry/IntRect.h"
19 19
20 namespace blink { 20 namespace blink {
21 21
22 using namespace HTMLNames; 22 using namespace HTMLNames;
23 23
24 // How long do we wait after a scroll event before deciding that no more
25 // scroll events are going to arrive?
26 static const double viewportTimerPollDelay = 0.5;
27
24 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element) 28 AutoplayExperimentHelper::AutoplayExperimentHelper(HTMLMediaElement& element)
25 : m_element(element) 29 : m_element(element)
26 , m_mode(AutoplayExperimentConfig::Mode::Off) 30 , m_mode(AutoplayExperimentConfig::Mode::Off)
27 , m_playPending(false) 31 , m_playPending(false)
32 , m_registeredWithView(false)
33 , m_wasInViewport(false)
34 , m_viewportTimer(this, &AutoplayExperimentHelper::viewportTimerFired)
28 { 35 {
29 if (document().settings()) { 36 if (document().settings()) {
30 m_mode = AutoplayExperimentConfig::fromString(document().settings()->aut oplayExperimentMode()); 37 m_mode = AutoplayExperimentConfig::fromString(document().settings()->aut oplayExperimentMode());
31 38
32 if (m_mode != AutoplayExperimentConfig::Mode::Off) { 39 if (m_mode != AutoplayExperimentConfig::Mode::Off) {
33 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d", 40 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to %d",
34 m_mode); 41 m_mode);
35 } 42 }
36 } 43 }
37 } 44 }
38 45
39 AutoplayExperimentHelper::~AutoplayExperimentHelper() 46 AutoplayExperimentHelper::~AutoplayExperimentHelper()
40 { 47 {
48 unregisterForPositionUpdatesIfNeeded();
41 } 49 }
42 50
43 void AutoplayExperimentHelper::becameReadyToPlay() 51 void AutoplayExperimentHelper::becameReadyToPlay()
44 { 52 {
45 // Assuming that we're eligible to override the user gesture requirement, 53 // Assuming that we're eligible to override the user gesture requirement,
46 // then play. 54 // either play if we meet the visibility checks, or install a listener
55 // to wait for them to pass.
47 if (isEligible()) { 56 if (isEligible()) {
48 prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately); 57 if (isInViewportIfNeeded())
58 prepareToPlay(GesturelessPlaybackStartedByAutoplayFlagImmediately);
59 else
60 registerForPositionUpdatesIfNeeded();
49 } 61 }
50 } 62 }
51 63
52 void AutoplayExperimentHelper::playMethodCalled() 64 void AutoplayExperimentHelper::playMethodCalled()
53 { 65 {
54 // Set the pending state, even if the play isn't going to be pending. 66 // Set the pending state, even if the play isn't going to be pending.
55 // Eligibility can change if, for example, the mute status changes. 67 // Eligibility can change if, for example, the mute status changes.
56 // Having this set is okay. 68 // Having this set is okay.
57 m_playPending = true; 69 m_playPending = true;
58 70
59 if (!UserGestureIndicator::processingUserGesture()) { 71 if (!UserGestureIndicator::processingUserGesture()) {
60 72
61 if (isEligible()) { 73 if (isEligible()) {
62 // Remember that userGestureRequiredForPlay is required for 74 // Remember that userGestureRequiredForPlay is required for
63 // us to be eligible for the experiment. 75 // us to be eligible for the experiment.
64 // We are able to override the gesture requirement now, so 76 // If we are able to override the gesture requirement now, then
65 // do so. 77 // do so. Otherwise, install an event listener if we need one.
66 prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately); 78 if (isInViewportIfNeeded()) {
79 // Override the gesture and play.
80 prepareToPlay(GesturelessPlaybackStartedByPlayMethodImmediately) ;
81 } else {
82 // Wait for viewport visibility.
83 registerForPositionUpdatesIfNeeded();
84 }
67 } 85 }
68 86
87 } else if (m_element.isUserGestureRequiredForPlay()) {
88 unregisterForPositionUpdatesIfNeeded();
69 } 89 }
70 } 90 }
71 91
72 void AutoplayExperimentHelper::pauseMethodCalled() 92 void AutoplayExperimentHelper::pauseMethodCalled()
73 { 93 {
74 // Don't try to autoplay, if we would have. 94 // Don't try to autoplay, if we would have.
75 m_playPending = false; 95 m_playPending = false;
96 unregisterForPositionUpdatesIfNeeded();
76 } 97 }
77 98
78 void AutoplayExperimentHelper::mutedChanged() 99 void AutoplayExperimentHelper::mutedChanged()
79 { 100 {
80 // In other words, start playing if we just needed 'mute' to autoplay. 101 // If we are no longer eligible for the autoplay experiment, then also
102 // quit listening for events. If we are eligible, and if we should be
103 // playing, then start playing. In other words, start playing if
104 // we just needed 'mute' to autoplay.
105 if (!isEligible()) {
106 unregisterForPositionUpdatesIfNeeded();
107 } else {
108 // Try to play. If we can't, then install a listener.
109 if (!maybeStartPlaying())
110 registerForPositionUpdatesIfNeeded();
111 }
112 }
113
114 void AutoplayExperimentHelper::registerForPositionUpdatesIfNeeded()
115 {
116 // If we don't require that the player is in the viewport, then we don't
117 // need the listener.
118 if (!enabled(AutoplayExperimentConfig::Mode::IfViewport))
119 return;
120
121 LayoutObject* layoutObject = m_element.layoutObject();
122 // TODO(liberato): update tests to include audio.
123 // TODO(liberato): fix visibility check for "onscreen".
124 if (layoutObject && layoutObject->isMedia()) {
125 LayoutMedia* layoutMedia = static_cast<LayoutMedia*>(layoutObject);
126 layoutMedia->setRequestPositionUpdates(true);
127 m_registeredWithView = true;
128 }
129 }
130
131 void AutoplayExperimentHelper::unregisterForPositionUpdatesIfNeeded()
132 {
133 if (m_registeredWithView) {
134 LayoutObject* obj = m_element.layoutObject();
135 if (obj && obj->isMedia()) {
136 LayoutMedia* layoutMedia = (LayoutMedia*)obj;
137 layoutMedia->setRequestPositionUpdates(false);
138 m_registeredWithView = false;
139 }
140 }
141 }
142
143 void AutoplayExperimentHelper::positionChanged()
144 {
145 // Reset the timer to indicate that scrolling has happened
146 // recently, and might still be ongoing.
147 // Also note that we are called quite often, including when the
148 // page becomes visible. That's why we don't bother to register
149 // for page visibility changes explicitly.
150
151 LocationState curLocation(m_element);
152 const bool inViewport = curLocation.isInViewport();
153 if (inViewport && !m_wasInViewport) {
154 // We have transitioned from not visible to visible. Reset the timer
155 // to check if we should start autoplay.
156 m_viewportTimer.startOneShot(viewportTimerPollDelay, FROM_HERE);
157 }
158 m_wasInViewport = inViewport;
159 }
160
161 void AutoplayExperimentHelper::triggerAutoplayViewportCheck()
162 {
163 viewportTimerFired(nullptr);
164 }
165
166 void AutoplayExperimentHelper::viewportTimerFired(Timer<AutoplayExperimentHelper >*)
167 {
168 // Sufficient time has passed since the last scroll that we'll
169 // treat it as the end of scroll. Autoplay if we should.
81 maybeStartPlaying(); 170 maybeStartPlaying();
82 } 171 }
83 172
173 bool AutoplayExperimentHelper::isInViewportIfNeeded()
174 {
175 // We could check for eligibility here, but we skip it. Some of our
176 // callers need to do it separately, and we don't want to check more
177 // than we need to.
178 // Also remember that page visibility is assumed for clank.
179
180 // If viewport visibility isn't required, then it's visible enough.
181 if (!enabled(AutoplayExperimentConfig::Mode::IfViewport))
182 return true;
183
184 return LocationState(m_element).isInViewport();
185 }
186
84 bool AutoplayExperimentHelper::maybeStartPlaying() 187 bool AutoplayExperimentHelper::maybeStartPlaying()
85 { 188 {
86 // See if we're allowed to autoplay now. 189 // See if we're allowed to autoplay now.
87 if (!isEligible()) { 190 if (!isEligible() || !isInViewportIfNeeded()) {
88 return false; 191 return false;
89 } 192 }
90 193
91 // Start playing! 194 // Start playing!
92 prepareToPlay(m_element.shouldAutoplay() 195 prepareToPlay(m_element.shouldAutoplay()
93 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll 196 ? GesturelessPlaybackStartedByAutoplayFlagAfterScroll
94 : GesturelessPlaybackStartedByPlayMethodAfterScroll); 197 : GesturelessPlaybackStartedByPlayMethodAfterScroll);
95 m_element.playInternal(); 198 m_element.playInternal();
96 199
97 return true; 200 return true;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 } 258 }
156 259
157 void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric) 260 void AutoplayExperimentHelper::prepareToPlay(AutoplayMetrics metric)
158 { 261 {
159 m_element.recordAutoplayMetric(metric); 262 m_element.recordAutoplayMetric(metric);
160 263
161 // This also causes !isEligible, so that we don't allow autoplay more than 264 // This also causes !isEligible, so that we don't allow autoplay more than
162 // once. Be sure to do this before muteIfNeeded(). 265 // once. Be sure to do this before muteIfNeeded().
163 m_element.removeUserGestureRequirement(); 266 m_element.removeUserGestureRequirement();
164 267
268 unregisterForPositionUpdatesIfNeeded();
165 muteIfNeeded(); 269 muteIfNeeded();
166 270
167 // Record that this autoplayed without a user gesture. This is normally 271 // Record that this autoplayed without a user gesture. This is normally
168 // set when we discover an autoplay attribute, but we include all cases 272 // set when we discover an autoplay attribute, but we include all cases
169 // where playback started without a user gesture, e.g., play(). 273 // where playback started without a user gesture, e.g., play().
170 m_element.setInitialPlayWithoutUserGestures(true); 274 m_element.setInitialPlayWithoutUserGestures(true);
171 275
172 // Do not actually start playback here. 276 // Do not actually start playback here.
173 } 277 }
174 278
175 Document& AutoplayExperimentHelper::document() const 279 Document& AutoplayExperimentHelper::document() const
176 { 280 {
177 return m_element.document(); 281 return m_element.document();
178 } 282 }
179 283
284 AutoplayExperimentHelper::LocationState::LocationState(Element& element)
285 : m_valid(false)
286 {
287 const LocalDOMWindow* domWindow = element.document().domWindow();
288 if (!domWindow)
289 return;
290
291 // Get the page visibility.
292 Frame* frame = domWindow->frame();
293 if (!frame)
294 return;
295
296 Page* page = frame->page();
297 if (!page)
298 return;
299
300 if (!element.layoutObject())
301 return;
302
303 const LayoutBox* elementBox = element.layoutObject()->enclosingBox();
304 if (!elementBox)
305 return;
306
307 float zoom = elementBox->style()->effectiveZoom();
308 IntRect us(elementBox->offsetLeft().toInt()
309 , elementBox->offsetTop().toInt()
310 , elementBox->clientWidth().toInt()
311 , elementBox->clientHeight().toInt());
312 IntRect screen(domWindow->scrollX()*zoom, domWindow->scrollY()*zoom, domWind ow->innerWidth()*zoom, domWindow->innerHeight()*zoom);
313
314 m_visibilityState = page->visibilityState();
315 m_element = us;
316 m_screen = screen;
317 m_valid = true;
180 } 318 }
319
320 bool AutoplayExperimentHelper::LocationState::isInViewport()
321 {
322 // Check if we're in the viewport.
323 // TODO(liberato): fix this.
324 return m_valid
325 && m_visibilityState == PageVisibilityStateVisible
326 && m_screen.contains(m_element);
327 }
328
329 bool AutoplayExperimentHelper::LocationState::operator==(const LocationState& th em) const
330 {
331 // If either state is not valid, then they are not equal.
332 return m_valid && them.valid()
333 && m_visibilityState == them.visibilityState()
334 && m_screen == them.screen()
335 && m_element == them.element();
336 }
337
338 bool AutoplayExperimentHelper::LocationState::operator!=(const LocationState& th em) const
339 {
340 return !((*this) == them);
341 }
342
343 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698