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

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

Issue 1179223002: Implement autoplay gesture override experiment. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: removed TODO that was already done. Created 5 years, 4 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 /* 1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 2 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 119
120 #ifndef LOG_CACHED_TIME_WARNINGS 120 #ifndef LOG_CACHED_TIME_WARNINGS
121 // Default to not logging warnings about excessive drift in the cached media tim e because it adds a 121 // Default to not logging warnings about excessive drift in the cached media tim e because it adds a
122 // fair amount of overhead and logging. 122 // fair amount of overhead and logging.
123 #define LOG_CACHED_TIME_WARNINGS 0 123 #define LOG_CACHED_TIME_WARNINGS 0
124 #endif 124 #endif
125 125
126 // URL protocol used to signal that the media source API is being used. 126 // URL protocol used to signal that the media source API is being used.
127 static const char mediaSourceBlobProtocol[] = "blob"; 127 static const char mediaSourceBlobProtocol[] = "blob";
128 128
129 // How often do we poll for scrolling stopped during a visibility check?
130 static const double visibilityTimerPollDelay = 0.5;
131 // How long do we repeat visibility checks? We will poll once every PollDelay.
132 // Note that we will stop checking if we don't detect scrolling, also.
133 static const double visibilityCheckDuration = 5;
134
129 using namespace HTMLNames; 135 using namespace HTMLNames;
130 136
131 typedef WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>> WeakMediaEle mentSet; 137 typedef WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>> WeakMediaEle mentSet;
132 typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet> DocumentElementSetMap; 138 typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet> DocumentElementSetMap;
133 static DocumentElementSetMap& documentToElementSetMap() 139 static DocumentElementSetMap& documentToElementSetMap()
134 { 140 {
135 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<DocumentElementSetMap>, map, (ado ptPtrWillBeNoop(new DocumentElementSetMap()))); 141 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<DocumentElementSetMap>, map, (ado ptPtrWillBeNoop(new DocumentElementSetMap())));
136 return *map; 142 return *map;
137 } 143 }
138 144
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
244 // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows 250 // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows
245 // it cannot render. 251 // it cannot render.
246 if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmp ty()) { 252 if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmp ty()) {
247 WebMimeRegistry::SupportsType supported = Platform::current()->mimeRegis try()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySystem.lower ()); 253 WebMimeRegistry::SupportsType supported = Platform::current()->mimeRegis try()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySystem.lower ());
248 return supported > WebMimeRegistry::IsNotSupported; 254 return supported > WebMimeRegistry::IsNotSupported;
249 } 255 }
250 256
251 return false; 257 return false;
252 } 258 }
253 259
254 // These values are used for a histogram. Do not reorder. 260 void HTMLMediaElement::recordAutoplayMetric(AutoplayMetrics metric)
255 enum AutoplayMetrics {
256 // Media element with autoplay seen.
257 AutoplayMediaFound = 0,
258 // Autoplay enabled and user stopped media play at any point.
259 AutoplayStopped = 1,
260 // Autoplay enabled but user bailed out on media play early.
261 AutoplayBailout = 2,
262 // Autoplay disabled but user manually started media.
263 AutoplayManualStart = 3,
264 // Autoplay was (re)enabled through a user-gesture triggered load()
265 AutoplayEnabledThroughLoad = 4,
266 // Autoplay disabled by sandbox flags.
267 AutoplayDisabledBySandbox = 5,
268 // This enum value must be last.
269 NumberOfAutoplayMetrics,
270 };
271
272 static void recordAutoplayMetric(AutoplayMetrics metric)
273 { 261 {
274 Platform::current()->histogramEnumeration("Blink.MediaElement.Autoplay", met ric, NumberOfAutoplayMetrics); 262 Platform::current()->histogramEnumeration("Blink.MediaElement.Autoplay", met ric, NumberOfAutoplayMetrics);
275 } 263 }
276 264
277 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem) 265 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem)
278 { 266 {
279 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); 267 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
280 268
281 if (!RuntimeEnabledFeatures::mediaEnabled()) 269 if (!RuntimeEnabledFeatures::mediaEnabled())
282 return WebMimeRegistry::IsNotSupported; 270 return WebMimeRegistry::IsNotSupported;
(...skipping 10 matching lines...) Expand all
293 // 4.8.10.3 MIME types - The canPlayType(type) method must return the empty string if type is a type that the 281 // 4.8.10.3 MIME types - The canPlayType(type) method must return the empty string if type is a type that the
294 // user agent knows it cannot render or is the type "application/octet-strea m" 282 // user agent knows it cannot render or is the type "application/octet-strea m"
295 if (type == "application/octet-stream") 283 if (type == "application/octet-stream")
296 return WebMimeRegistry::IsNotSupported; 284 return WebMimeRegistry::IsNotSupported;
297 285
298 return Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, type Codecs, system); 286 return Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, type Codecs, system);
299 } 287 }
300 288
301 URLRegistry* HTMLMediaElement::s_mediaStreamRegistry = 0; 289 URLRegistry* HTMLMediaElement::s_mediaStreamRegistry = 0;
302 290
291 class HTMLMediaElement::AutoplayExperimentTouchListener : public EventListener {
292 public:
293 AutoplayExperimentTouchListener(HTMLMediaElement* element) : EventListen er(CPPEventListenerType), m_element(element) { }
294 virtual bool operator==(const EventListener& them)
295 {
296 return &them == this;
297 }
298
299 void handleEvent(ExecutionContext*, Event*) override
300 {
301 if (m_element)
302 m_element->beginPeriodicVisibilityCheck();
303 }
304
305 private:
306 HTMLMediaElement* m_element;
307 };
308
303 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry) 309 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry)
304 { 310 {
305 ASSERT(!s_mediaStreamRegistry); 311 ASSERT(!s_mediaStreamRegistry);
306 s_mediaStreamRegistry = registry; 312 s_mediaStreamRegistry = registry;
307 } 313 }
308 314
309 bool HTMLMediaElement::isMediaStreamURL(const String& url) 315 bool HTMLMediaElement::isMediaStreamURL(const String& url)
310 { 316 {
311 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false; 317 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false;
312 } 318 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 , m_haveVisibleTextTrack(false) 363 , m_haveVisibleTextTrack(false)
358 , m_processingPreferenceChange(false) 364 , m_processingPreferenceChange(false)
359 , m_remoteRoutesAvailable(false) 365 , m_remoteRoutesAvailable(false)
360 , m_playingRemotely(false) 366 , m_playingRemotely(false)
361 , m_isFinalizing(false) 367 , m_isFinalizing(false)
362 , m_initialPlayWithoutUserGestures(false) 368 , m_initialPlayWithoutUserGestures(false)
363 , m_autoplayMediaCounted(false) 369 , m_autoplayMediaCounted(false)
364 , m_audioTracks(AudioTrackList::create(*this)) 370 , m_audioTracks(AudioTrackList::create(*this))
365 , m_videoTracks(VideoTrackList::create(*this)) 371 , m_videoTracks(VideoTrackList::create(*this))
366 , m_textTracks(nullptr) 372 , m_textTracks(nullptr)
373 , m_autoplayExperimentPlayPending(false)
374 , m_autoplayExperimentStartedByExperiment(false)
375 , m_autoplayExperimentMode(ExperimentOff)
367 #if ENABLE(WEB_AUDIO) 376 #if ENABLE(WEB_AUDIO)
368 , m_audioSourceNode(nullptr) 377 , m_audioSourceNode(nullptr)
369 #endif 378 #endif
379 , m_autoplayVisibilityTimer(this, &HTMLMediaElement::visibilityTimerFired)
380 , m_autoplayLastScrollX(std::numeric_limits<double>::quiet_NaN())
381 , m_autoplayLastScrollY(std::numeric_limits<double>::quiet_NaN())
382 , m_autoplayVisibilityTimerSpan(0)
370 { 383 {
371 #if ENABLE(OILPAN) 384 #if ENABLE(OILPAN)
372 ThreadState::current()->registerPreFinalizer(this); 385 ThreadState::current()->registerPreFinalizer(this);
373 #endif 386 #endif
374 ASSERT(RuntimeEnabledFeatures::mediaEnabled()); 387 ASSERT(RuntimeEnabledFeatures::mediaEnabled());
375 388
376 WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this); 389 WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this);
377 390
378 if (document.settings() && document.settings()->mediaPlaybackRequiresUserGes ture()) 391 if (document.settings()) {
philipj_slow 2015/08/05 10:03:11 Wow, this has actually been broken the whole time,
liberato (no reviews please) 2015/08/06 06:37:58 i'm glad you're one of the good guys.
379 m_userGestureRequiredForPlay = true; 392 if (document.settings()->mediaPlaybackRequiresUserGesture())
393 m_userGestureRequiredForPlay = true;
394
395 const String& autoplayMode = document.settings()->autoplayExperimentMode ();
396 if (autoplayMode.contains("enabled")) {
397 // Autoplay with no gesture requirement.
398 m_autoplayExperimentMode |= ExperimentEnabled;
399 }
400 if (autoplayMode.contains("-ifvisible")) {
401 // Override gesture requirement only if the player is visible.
402 m_autoplayExperimentMode |= ExperimentIfVisible;
403 }
404 if (autoplayMode.contains("-ifmuted")) {
405 // Override gesture requirement only if the media is muted or has
406 // no audio track.
407 m_autoplayExperimentMode |= ExperimentIfMuted;
408 }
409 if (autoplayMode.contains("-ifmobile")) {
410 // Override gesture requirement only if the page is optimized
411 // for mobile.
412 m_autoplayExperimentMode |= ExperimentIfMobile;
413 }
414 if (autoplayMode.contains("-playmuted")) {
415 m_autoplayExperimentMode |= ExperimentPlayMuted;
416 }
417
418 if (m_autoplayExperimentMode != ExperimentOff) {
419 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to '%s' (% d)",
420 autoplayMode.ascii().data(), m_autoplayExperimentMode);
421 }
422 }
380 423
381 setHasCustomStyleCallbacks(); 424 setHasCustomStyleCallbacks();
382 addElementToDocumentMap(this, &document); 425 addElementToDocumentMap(this, &document);
383 } 426 }
384 427
385 HTMLMediaElement::~HTMLMediaElement() 428 HTMLMediaElement::~HTMLMediaElement()
386 { 429 {
387 WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this); 430 WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this);
431
432 autoplayExperimentClearEventListenerIfNeeded();
433
388 #if !ENABLE(OILPAN) 434 #if !ENABLE(OILPAN)
389 // HTMLMediaElement and m_asyncEventQueue always become unreachable 435 // HTMLMediaElement and m_asyncEventQueue always become unreachable
390 // together. So HTMLMediaElement and m_asyncEventQueue are destructed in 436 // together. So HTMLMediaElement and m_asyncEventQueue are destructed in
391 // the same GC. We don't need to close it explicitly in Oilpan. 437 // the same GC. We don't need to close it explicitly in Oilpan.
392 m_asyncEventQueue->close(); 438 m_asyncEventQueue->close();
393 439
394 setShouldDelayLoadEvent(false); 440 setShouldDelayLoadEvent(false);
395 441
396 if (m_textTracks) 442 if (m_textTracks)
397 m_textTracks->clearOwner(); 443 m_textTracks->clearOwner();
(...skipping 1150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1548 scheduleEvent(EventTypeNames::canplay); 1594 scheduleEvent(EventTypeNames::canplay);
1549 if (isPotentiallyPlaying) 1595 if (isPotentiallyPlaying)
1550 scheduleEvent(EventTypeNames::playing); 1596 scheduleEvent(EventTypeNames::playing);
1551 } 1597 }
1552 1598
1553 if (m_autoplaying && m_paused && autoplay()) { 1599 if (m_autoplaying && m_paused && autoplay()) {
1554 autoplayMediaEncountered(); 1600 autoplayMediaEncountered();
1555 1601
1556 if (document().isSandboxed(SandboxAutomaticFeatures)) { 1602 if (document().isSandboxed(SandboxAutomaticFeatures)) {
1557 recordAutoplayMetric(AutoplayDisabledBySandbox); 1603 recordAutoplayMetric(AutoplayDisabledBySandbox);
1558 } else if (!m_userGestureRequiredForPlay) { 1604 } else {
1559 m_paused = false; 1605 // If the autoplay experiment says that it's okay to play now,
1560 invalidateCachedTime(); 1606 // then don't require a user gesture.
1561 scheduleEvent(EventTypeNames::play); 1607 if (autoplayExperimentIsEligible()) {
1562 scheduleEvent(EventTypeNames::playing); 1608 if (autoplayExperimentIsVisible()) {
philipj_slow 2015/08/05 10:03:11 Perhaps rename this so that it's clear that it alw
liberato (no reviews please) 2015/08/06 06:37:57 Done.
1609 autoplayExperimentPrepareToPlay(AutoplayExperimentStarte dByLoad);
1610 // Will play below.
1611 } else {
1612 // Wait for visibility checks to pass.
1613 autoplayExperimentInstallEventListenerIfNeeded();
1614 }
1615 }
1616
1617 if (!m_userGestureRequiredForPlay) {
1618 m_paused = false;
philipj_slow 2015/08/05 10:03:11 This branch was previously predicated on the sandb
liberato (no reviews please) 2015/08/06 06:37:57 it still is, though it's virtually impossible to t
1619 invalidateCachedTime();
1620 scheduleEvent(EventTypeNames::play);
1621 scheduleEvent(EventTypeNames::playing);
1622 }
1563 } 1623 }
1564 } 1624 }
1565 1625
1566 scheduleEvent(EventTypeNames::canplaythrough); 1626 scheduleEvent(EventTypeNames::canplaythrough);
1567 1627
1568 shouldUpdateDisplayState = true; 1628 shouldUpdateDisplayState = true;
1569 } 1629 }
1570 1630
1571 if (shouldUpdateDisplayState) { 1631 if (shouldUpdateDisplayState) {
1572 updateDisplayState(); 1632 updateDisplayState();
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
1953 2013
1954 WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const 2014 WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const
1955 { 2015 {
1956 return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType(); 2016 return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType();
1957 } 2017 }
1958 2018
1959 void HTMLMediaElement::play() 2019 void HTMLMediaElement::play()
1960 { 2020 {
1961 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this); 2021 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this);
1962 2022
2023 // Set the pending state, even if the play isn't going to be pending.
2024 // Eligibility can change if, for example, the mute status changes.
2025 // Having this set is okay.
2026 m_autoplayExperimentPlayPending = true;
2027
1963 if (!UserGestureIndicator::processingUserGesture()) { 2028 if (!UserGestureIndicator::processingUserGesture()) {
1964 autoplayMediaEncountered(); 2029 autoplayMediaEncountered();
2030
2031 if (autoplayExperimentIsEligible()) {
2032 // Remember that m_userGestureRequiredForPlay is required for
2033 // us to be eligible for the experiment.
2034 // If we are able to override the gesture requirement now, then
2035 // do so. Otherwise, install an event listener if we need one.
2036 if (autoplayExperimentIsVisible()) {
2037 // Override the gesture and play.
2038 autoplayExperimentPrepareToPlay(AutoplayExperimentStartedByPlay) ;
2039 } else {
2040 // Wait for visibility.
2041 autoplayExperimentInstallEventListenerIfNeeded();
2042 }
2043 }
2044
1965 if (m_userGestureRequiredForPlay) { 2045 if (m_userGestureRequiredForPlay) {
2046 recordAutoplayMetric(AutoplayPlayFailed);
1966 String message = ExceptionMessages::failedToExecute("play", "HTMLMed iaElement", "API can only be initiated by a user gesture."); 2047 String message = ExceptionMessages::failedToExecute("play", "HTMLMed iaElement", "API can only be initiated by a user gesture.");
1967 document().executionContext()->addConsoleMessage(ConsoleMessage::cre ate(JSMessageSource, WarningMessageLevel, message)); 2048 document().executionContext()->addConsoleMessage(ConsoleMessage::cre ate(JSMessageSource, WarningMessageLevel, message));
1968 return; 2049 return;
1969 } 2050 }
1970 } else if (m_userGestureRequiredForPlay) { 2051 } else if (m_userGestureRequiredForPlay) {
1971 if (m_autoplayMediaCounted) 2052 if (m_autoplayMediaCounted)
1972 recordAutoplayMetric(AutoplayManualStart); 2053 recordAutoplayMetric(AutoplayManualStart);
1973 m_userGestureRequiredForPlay = false; 2054 m_userGestureRequiredForPlay = false;
2055 autoplayExperimentClearEventListenerIfNeeded();
1974 } 2056 }
1975 2057
1976 playInternal(); 2058 playInternal();
1977 } 2059 }
1978 2060
1979 void HTMLMediaElement::playInternal() 2061 void HTMLMediaElement::playInternal()
1980 { 2062 {
1981 WTF_LOG(Media, "HTMLMediaElement::playInternal(%p)", this); 2063 WTF_LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
1982 2064
1983 // 4.8.10.9. Playing the media resource 2065 // 4.8.10.9. Playing the media resource
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
2025 ASSERT(m_initialPlayWithoutUserGestures); 2107 ASSERT(m_initialPlayWithoutUserGestures);
2026 m_initialPlayWithoutUserGestures = false; 2108 m_initialPlayWithoutUserGestures = false;
2027 2109
2028 recordAutoplayMetric(AutoplayStopped); 2110 recordAutoplayMetric(AutoplayStopped);
2029 2111
2030 // We count the user as having bailed-out on the video if they watched 2112 // We count the user as having bailed-out on the video if they watched
2031 // less than one minute and less than 50% of it. 2113 // less than one minute and less than 50% of it.
2032 double playedTime = currentTime(); 2114 double playedTime = currentTime();
2033 if (playedTime < 60) { 2115 if (playedTime < 60) {
2034 double progress = playedTime / duration(); 2116 double progress = playedTime / duration();
2035 if (progress < 0.5) 2117 if (progress < 0.5) {
philipj_slow 2015/08/05 10:03:11 Why the added {}?
liberato (no reviews please) 2015/08/06 06:37:57 whoops, thanks -- i had more code in there and for
2036 recordAutoplayMetric(AutoplayBailout); 2118 recordAutoplayMetric(AutoplayBailout);
2119 }
2037 } 2120 }
2038 } 2121 }
2039 2122
2040 void HTMLMediaElement::pause() 2123 void HTMLMediaElement::pause()
2041 { 2124 {
2042 WTF_LOG(Media, "HTMLMediaElement::pause(%p)", this); 2125 WTF_LOG(Media, "HTMLMediaElement::pause(%p)", this);
2043 2126
2044 if (m_networkState == NETWORK_EMPTY) 2127 if (m_networkState == NETWORK_EMPTY)
2045 scheduleDelayedAction(LoadMediaResource); 2128 scheduleDelayedAction(LoadMediaResource);
2046 2129
2130 // Don't try to autoplay, if we would have.
2131 m_autoplayExperimentPlayPending = false;
2132 autoplayExperimentClearEventListenerIfNeeded();
2133
2047 m_autoplaying = false; 2134 m_autoplaying = false;
2048 2135
2049 if (!m_paused) { 2136 if (!m_paused) {
2050 if (m_initialPlayWithoutUserGestures) 2137 if (m_initialPlayWithoutUserGestures)
2051 gesturelessInitialPlayHalted(); 2138 gesturelessInitialPlayHalted();
2052 2139
2053 m_paused = true; 2140 m_paused = true;
2054 scheduleTimeupdateEvent(false); 2141 scheduleTimeupdateEvent(false);
2055 scheduleEvent(EventTypeNames::pause); 2142 scheduleEvent(EventTypeNames::pause);
2056 } 2143 }
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
2134 2221
2135 void HTMLMediaElement::setMuted(bool muted) 2222 void HTMLMediaElement::setMuted(bool muted)
2136 { 2223 {
2137 WTF_LOG(Media, "HTMLMediaElement::setMuted(%p, %s)", this, boolString(muted) ); 2224 WTF_LOG(Media, "HTMLMediaElement::setMuted(%p, %s)", this, boolString(muted) );
2138 2225
2139 if (m_muted == muted) 2226 if (m_muted == muted)
2140 return; 2227 return;
2141 2228
2142 m_muted = muted; 2229 m_muted = muted;
2143 2230
2231 // If we are no longer eligible for the autoplay experiment, then also
2232 // quit listening for events. If we are eligible, and if we should be
2233 // playing, then start playing. In other words, start playing if
philipj_slow 2015/08/05 10:03:11 Really start playing by setting the muted attribut
liberato (no reviews please) 2015/08/06 06:37:57 there was interest in this from one of the origina
philipj_slow 2015/08/13 09:27:53 Acknowledged.
2234 // we just needed 'mute' to autoplay.
2235 if (!autoplayExperimentIsEligible()) {
2236 autoplayExperimentClearEventListenerIfNeeded();
2237 } else {
2238 // Try to play. If we can't, then install a visibility listener.
2239 if (!autoplayExperimentMaybeStartPlaying())
2240 autoplayExperimentInstallEventListenerIfNeeded();
2241 }
2242
2144 updateVolume(); 2243 updateVolume();
2145 2244
2146 scheduleEvent(EventTypeNames::volumechange); 2245 scheduleEvent(EventTypeNames::volumechange);
2147 } 2246 }
2148 2247
2149 void HTMLMediaElement::updateVolume() 2248 void HTMLMediaElement::updateVolume()
2150 { 2249 {
2151 if (webMediaPlayer()) 2250 if (webMediaPlayer())
2152 webMediaPlayer()->setVolume(effectiveMediaVolume()); 2251 webMediaPlayer()->setVolume(effectiveMediaVolume());
2153 2252
(...skipping 1628 matching lines...) Expand 10 before | Expand all | Expand 10 after
3782 { 3881 {
3783 visitor->trace(m_client); 3882 visitor->trace(m_client);
3784 } 3883 }
3785 3884
3786 DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl) 3885 DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl)
3787 { 3886 {
3788 visitor->trace(m_client); 3887 visitor->trace(m_client);
3789 } 3888 }
3790 #endif 3889 #endif
3791 3890
3891 void HTMLMediaElement::autoplayExperimentInstallEventListenerIfNeeded()
3892 {
3893 // If we don't require visibility, then we don't need the listener.
3894 if (!(m_autoplayExperimentMode & ExperimentIfVisible))
3895 return;
3896
3897 if (document().domWindow() && !m_autoplayExperimentTouchListener) {
3898 m_autoplayExperimentTouchListener = adoptRef(new AutoplayExperimentTouch Listener(this));
3899 // Listen for events that might show a user-initiated scroll. We
3900 // don't try to catch programmatic scrolls right now.
3901 document().domWindow()->addEventListener("touchend", m_autoplayExperimen tTouchListener, false);
3902 document().domWindow()->addEventListener("touchcancel", m_autoplayExperi mentTouchListener, false);
3903 }
3792 } 3904 }
3905
3906 void HTMLMediaElement::autoplayExperimentClearEventListenerIfNeeded()
3907 {
3908 if (m_autoplayExperimentTouchListener) {
3909 LocalDOMWindow* domWindow = document().domWindow();
3910 if (domWindow) {
3911 domWindow->removeEventListener("touchend", m_autoplayExperimentTouch Listener, false);
3912 domWindow->removeEventListener("touchcancel", m_autoplayExperimentTo uchListener, false);
3913 }
3914 // Either way, clear our ref.
3915 m_autoplayExperimentTouchListener.clear();
3916 }
3917 }
3918
3919 void HTMLMediaElement::beginPeriodicVisibilityCheck()
3920 {
3921 // Note that a visibility check might already be in progress.
3922 // Always reset the span of the checks to maximum.
3923 m_autoplayVisibilityTimerSpan = visibilityCheckDuration;
3924
3925 // If the timer isn't active, then fire the timer immediately to reset
3926 // it. We might just setOneShot(0).
3927 if (!m_autoplayVisibilityTimer.isActive()) {
3928 visibilityTimerFired(0);
3929 }
3930 }
3931
3932 void HTMLMediaElement::visibilityTimerFired(Timer<HTMLMediaElement>*)
3933 {
3934 const LocalDOMWindow* domWindow = document().domWindow();
3935 if (!domWindow)
3936 return;
3937
3938 const int currentScrollX = domWindow->scrollX();
3939 const int currentScrollY = domWindow->scrollY();
3940
3941 if (currentScrollX != m_autoplayLastScrollX
3942 || currentScrollY != m_autoplayLastScrollY) {
3943 // Still scrolling, so wait a bit more.
3944 m_autoplayLastScrollX = currentScrollX;
3945 m_autoplayLastScrollY = currentScrollY;
3946
3947 // Reset the timer to check again if we haven't tried for long enough.
3948 m_autoplayVisibilityTimerSpan -= visibilityTimerPollDelay;
3949 if (m_autoplayVisibilityTimerSpan >= 0) {
3950 m_autoplayVisibilityTimer.startOneShot(visibilityTimerPollDelay, FRO M_HERE);
3951 }
3952 } else {
3953 // No longer scrolling, so check visibility and stop.
3954 autoplayExperimentMaybeStartPlaying();
3955 }
3956 }
3957
3958 bool HTMLMediaElement::autoplayExperimentIsVisible()
3959 {
3960 // We could check for eligibility here, but we skip it. Some of our
3961 // callers need to do it separately, and we don't want to check more
3962 // than we need to.
3963
3964 // If visibility isn't required, then it's visible enough.
3965 if (!(m_autoplayExperimentMode & ExperimentIfVisible))
3966 return true;
3967
3968 // Autoplay is requested, and we're willing to override if the visibility
3969 // requirements are met.
3970
3971 // Check visibility.
philipj_slow 2015/08/05 10:03:11 A page visibility check is missing, this ought to
liberato (no reviews please) 2015/08/06 06:37:57 i don't think it'll ever play unless in the foregr
philipj_slow 2015/08/13 09:27:53 What is it that would prevent playback when not in
liberato (no reviews please) 2015/09/01 06:54:19 Done.
3972 const LocalDOMWindow* domWindow = document().domWindow();
3973 if (!domWindow)
3974 return false;
3975
3976 FloatRect us(offsetLeft(), offsetTop(), clientWidth(), clientHeight());
3977 FloatRect screen(domWindow->scrollX(), domWindow->scrollY(), domWindow->inne rWidth(), domWindow->innerHeight());
3978
3979 return screen.contains(us);
3980 }
3981
3982 bool HTMLMediaElement::autoplayExperimentMaybeStartPlaying()
3983 {
3984 // Make sure that we're eligible and visible.
3985 if (!autoplayExperimentIsEligible() || !autoplayExperimentIsVisible()) {
3986 return false;
3987 }
3988
3989 // Start playing!
3990 autoplayExperimentPrepareToPlay(AutoplayExperimentStartedByScroll);
3991 // Why are we always preparing? Just go!
philipj_slow 2015/08/05 10:03:12 Is this a question to the reviewer, or a rhetorica
liberato (no reviews please) 2015/08/06 06:37:57 random movie quotes make reviewing more fun! but
philipj_slow 2015/08/13 09:27:53 Ah, Spaceballs :)
3992 playInternal();
3993
3994 return true;
3995 }
3996
3997 bool HTMLMediaElement::autoplayExperimentIsEligible() const
3998 {
3999 // If no user gesture is required, then the experiment doesn't apply.
4000 // This is what prevents us from starting playback more than once.
4001 // Since this flag is never set to true once it's cleared, it will block
4002 // the autoplay experiment forever.
4003 if (!m_userGestureRequiredForPlay)
4004 return false;
4005
4006 if (m_autoplayExperimentMode == ExperimentOff)
4007 return false;
4008
4009 // If nobody has requested playback, either by the autoplay attribute or
4010 // a play() call, then do nothing.
4011 if (!m_autoplayExperimentPlayPending && !autoplay())
4012 return false;
4013
4014 // If the video is already playing, then do nothing. Note that there
4015 // is not a path where a user gesture is required but the video is
4016 // playing. However, we check for completeness.
4017 if (!m_paused)
4018 return false;
4019
4020 // Note that the viewport test always returns false on desktop, which is
4021 // why video-autoplay-experiment.html doesn't check -ifmobile .
4022 if (m_autoplayExperimentMode & ExperimentIfMobile) {
4023 if (!document().viewportDescription().isLegacyViewportType())
4024 return false;
4025 }
4026
4027 if (m_autoplayExperimentMode & ExperimentIfMuted) {
4028 // If media is muted, then autoplay when it comes into view.
4029 return fastHasAttribute(mutedAttr) || m_muted;
4030 }
4031
4032 // Autoplay when it comes into view (if needed), maybe muted.
4033 return true;
4034 }
4035
4036 void HTMLMediaElement::autoplayExperimentMuteIfNeeded()
4037 {
4038 if (m_autoplayExperimentMode & ExperimentPlayMuted) {
4039 m_muted = true;
4040 updateVolume();
4041 }
4042 }
4043
4044 void HTMLMediaElement::autoplayExperimentPrepareToPlay(AutoplayMetrics metric)
4045 {
4046 recordAutoplayMetric(metric);
4047
4048 // This also causes !autoplayExperimentIsEligible, so that we don't
4049 // allow autoplay more than once.
4050 m_userGestureRequiredForPlay = false;
4051
4052 m_autoplayExperimentStartedByExperiment = true;
4053 autoplayExperimentClearEventListenerIfNeeded();
4054 autoplayExperimentMuteIfNeeded();
4055
4056 // Record that this autoplayed without a user gesture. This is normally
4057 // set when we discover an autoplay attribute, but we include all cases
4058 // where playback started without a user gesture, e.g., play().
4059 m_initialPlayWithoutUserGestures = true;
4060 }
4061
4062 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698