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

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: CL feedback -- check scroll events, rekerfuddled the metrics a bit. 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
« no previous file with comments | « Source/core/html/HTMLMediaElement.h ('k') | Source/core/testing/Internals.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 long do we wait after a scroll event before deciding that no more
130 // scroll events are going to arrive?
131 static const double viewportTimerPollDelay = 0.3;
132
129 using namespace HTMLNames; 133 using namespace HTMLNames;
130 134
131 typedef WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>> WeakMediaEle mentSet; 135 typedef WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>> WeakMediaEle mentSet;
132 typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet> DocumentElementSetMap; 136 typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet> DocumentElementSetMap;
133 static DocumentElementSetMap& documentToElementSetMap() 137 static DocumentElementSetMap& documentToElementSetMap()
134 { 138 {
135 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<DocumentElementSetMap>, map, (ado ptPtrWillBeNoop(new DocumentElementSetMap()))); 139 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<DocumentElementSetMap>, map, (ado ptPtrWillBeNoop(new DocumentElementSetMap())));
136 return *map; 140 return *map;
137 } 141 }
138 142
(...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 248 // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows
245 // it cannot render. 249 // it cannot render.
246 if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmp ty()) { 250 if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmp ty()) {
247 WebMimeRegistry::SupportsType supported = Platform::current()->mimeRegis try()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySystem.lower ()); 251 WebMimeRegistry::SupportsType supported = Platform::current()->mimeRegis try()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySystem.lower ());
248 return supported > WebMimeRegistry::IsNotSupported; 252 return supported > WebMimeRegistry::IsNotSupported;
249 } 253 }
250 254
251 return false; 255 return false;
252 } 256 }
253 257
254 // These values are used for a histogram. Do not reorder. 258 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 { 259 {
274 Platform::current()->histogramEnumeration("Blink.MediaElement.Autoplay", met ric, NumberOfAutoplayMetrics); 260 Platform::current()->histogramEnumeration("Blink.MediaElement.Autoplay", met ric, NumberOfAutoplayMetrics);
275 } 261 }
276 262
277 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem) 263 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem)
278 { 264 {
279 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); 265 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
280 266
281 if (!RuntimeEnabledFeatures::mediaEnabled()) 267 if (!RuntimeEnabledFeatures::mediaEnabled())
282 return WebMimeRegistry::IsNotSupported; 268 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 279 // 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" 280 // user agent knows it cannot render or is the type "application/octet-strea m"
295 if (type == "application/octet-stream") 281 if (type == "application/octet-stream")
296 return WebMimeRegistry::IsNotSupported; 282 return WebMimeRegistry::IsNotSupported;
297 283
298 return Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, type Codecs, system); 284 return Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, type Codecs, system);
299 } 285 }
300 286
301 URLRegistry* HTMLMediaElement::s_mediaStreamRegistry = 0; 287 URLRegistry* HTMLMediaElement::s_mediaStreamRegistry = 0;
302 288
289 class HTMLMediaElement::AutoplayExperimentTouchListener : public EventListener {
290 public:
291 AutoplayExperimentTouchListener(HTMLMediaElement* element) : EventListen er(CPPEventListenerType), m_element(element) { }
292 virtual bool operator==(const EventListener& them)
293 {
294 return &them == this;
295 }
296
297 void handleEvent(ExecutionContext*, Event*) override
298 {
299 if (m_element)
300 m_element->notifyScrolled();
301 }
302
303 private:
304 HTMLMediaElement* m_element;
305 };
306
303 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry) 307 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry)
304 { 308 {
305 ASSERT(!s_mediaStreamRegistry); 309 ASSERT(!s_mediaStreamRegistry);
306 s_mediaStreamRegistry = registry; 310 s_mediaStreamRegistry = registry;
307 } 311 }
308 312
309 bool HTMLMediaElement::isMediaStreamURL(const String& url) 313 bool HTMLMediaElement::isMediaStreamURL(const String& url)
310 { 314 {
311 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false; 315 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false;
312 } 316 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 , m_haveVisibleTextTrack(false) 361 , m_haveVisibleTextTrack(false)
358 , m_processingPreferenceChange(false) 362 , m_processingPreferenceChange(false)
359 , m_remoteRoutesAvailable(false) 363 , m_remoteRoutesAvailable(false)
360 , m_playingRemotely(false) 364 , m_playingRemotely(false)
361 , m_isFinalizing(false) 365 , m_isFinalizing(false)
362 , m_initialPlayWithoutUserGestures(false) 366 , m_initialPlayWithoutUserGestures(false)
363 , m_autoplayMediaCounted(false) 367 , m_autoplayMediaCounted(false)
364 , m_audioTracks(AudioTrackList::create(*this)) 368 , m_audioTracks(AudioTrackList::create(*this))
365 , m_videoTracks(VideoTrackList::create(*this)) 369 , m_videoTracks(VideoTrackList::create(*this))
366 , m_textTracks(nullptr) 370 , m_textTracks(nullptr)
371 , m_autoplayExperimentPlayPending(false)
372 , m_autoplayExperimentStartedByExperiment(false)
373 , m_autoplayExperimentMode(ExperimentOff)
367 #if ENABLE(WEB_AUDIO) 374 #if ENABLE(WEB_AUDIO)
368 , m_audioSourceNode(nullptr) 375 , m_audioSourceNode(nullptr)
369 #endif 376 #endif
377 , m_autoplayViewportTimer(this, &HTMLMediaElement::viewportTimerFired)
378 , m_autoplayLastScrollX(std::numeric_limits<double>::quiet_NaN())
379 , m_autoplayLastScrollY(std::numeric_limits<double>::quiet_NaN())
380 , m_autoplayViewportTimerSpan(0)
370 { 381 {
371 #if ENABLE(OILPAN) 382 #if ENABLE(OILPAN)
372 ThreadState::current()->registerPreFinalizer(this); 383 ThreadState::current()->registerPreFinalizer(this);
373 #endif 384 #endif
374 ASSERT(RuntimeEnabledFeatures::mediaEnabled()); 385 ASSERT(RuntimeEnabledFeatures::mediaEnabled());
375 386
376 WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this); 387 WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this);
377 388
378 if (document.settings() && document.settings()->mediaPlaybackRequiresUserGes ture()) 389 if (document.settings()) {
379 m_userGestureRequiredForPlay = true; 390 if (document.settings()->mediaPlaybackRequiresUserGesture())
391 m_userGestureRequiredForPlay = true;
392
393 const String& autoplayMode = document.settings()->autoplayExperimentMode ();
394 if (autoplayMode.contains("enabled")) {
395 // Autoplay with no gesture requirement.
396 m_autoplayExperimentMode |= ExperimentEnabled;
397 }
398 if (autoplayMode.contains("-ifviewport")) {
399 // Override gesture requirement only if the player is within the
400 // current viewport.
401 m_autoplayExperimentMode |= ExperimentIfViewport;
402 }
403 if (autoplayMode.contains("-ifmuted")) {
404 // Override gesture requirement only if the media is muted or has
405 // no audio track.
406 m_autoplayExperimentMode |= ExperimentIfMuted;
407 }
408 if (autoplayMode.contains("-ifmobile")) {
409 // Override gesture requirement only if the page is optimized
410 // for mobile.
411 m_autoplayExperimentMode |= ExperimentIfMobile;
412 }
413 if (autoplayMode.contains("-playmuted")) {
414 m_autoplayExperimentMode |= ExperimentPlayMuted;
415 }
416
417 if (m_autoplayExperimentMode != ExperimentOff) {
418 WTF_LOG(Media, "HTMLMediaElement: autoplay experiment set to '%s' (% d)",
419 autoplayMode.ascii().data(), m_autoplayExperimentMode);
420 }
421 }
380 422
381 setHasCustomStyleCallbacks(); 423 setHasCustomStyleCallbacks();
382 addElementToDocumentMap(this, &document); 424 addElementToDocumentMap(this, &document);
383 } 425 }
384 426
385 HTMLMediaElement::~HTMLMediaElement() 427 HTMLMediaElement::~HTMLMediaElement()
386 { 428 {
387 WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this); 429 WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this);
430
431 autoplayExperimentClearEventListenerIfNeeded();
432
388 #if !ENABLE(OILPAN) 433 #if !ENABLE(OILPAN)
389 // HTMLMediaElement and m_asyncEventQueue always become unreachable 434 // HTMLMediaElement and m_asyncEventQueue always become unreachable
390 // together. So HTMLMediaElement and m_asyncEventQueue are destructed in 435 // together. So HTMLMediaElement and m_asyncEventQueue are destructed in
391 // the same GC. We don't need to close it explicitly in Oilpan. 436 // the same GC. We don't need to close it explicitly in Oilpan.
392 m_asyncEventQueue->close(); 437 m_asyncEventQueue->close();
393 438
394 setShouldDelayLoadEvent(false); 439 setShouldDelayLoadEvent(false);
395 440
396 if (m_textTracks) 441 if (m_textTracks)
397 m_textTracks->clearOwner(); 442 m_textTracks->clearOwner();
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 case WebMimeRegistry::IsSupported: 732 case WebMimeRegistry::IsSupported:
688 canPlay = "probably"; 733 canPlay = "probably";
689 break; 734 break;
690 } 735 }
691 736
692 WTF_LOG(Media, "HTMLMediaElement::canPlayType(%p, %s, %s) -> %s", this, mime Type.utf8().data(), keySystem.utf8().data(), canPlay.utf8().data()); 737 WTF_LOG(Media, "HTMLMediaElement::canPlayType(%p, %s, %s) -> %s", this, mime Type.utf8().data(), keySystem.utf8().data(), canPlay.utf8().data());
693 738
694 return canPlay; 739 return canPlay;
695 } 740 }
696 741
742 void HTMLMediaElement::recordMetricsIfStopping()
743 {
744 // If not playing, then nothing to record.
745 if (!m_playing)
746 return;
747
748 const bool bailout = isBailout();
749
750 // Record that play was stopped. We don't care if it was autoplay,
751 // play(), or the user manually started it.
752 recordAutoplayMetric(AnyPlaybackStopped);
753 if (bailout)
754 recordAutoplayMetric(AnyPlaybackBailout);
755
756 // If this was a gestureless play, then record that separately.
757 // These cover attr and play() gestureless starts.
758 if (m_initialPlayWithoutUserGestures) {
759 m_initialPlayWithoutUserGestures = false;
760
761 recordAutoplayMetric(AutoplayStopped);
762
763 if (bailout)
764 recordAutoplayMetric(AutoplayBailout);
765 }
766 }
767
697 void HTMLMediaElement::load() 768 void HTMLMediaElement::load()
698 { 769 {
699 WTF_LOG(Media, "HTMLMediaElement::load(%p)", this); 770 WTF_LOG(Media, "HTMLMediaElement::load(%p)", this);
700 771
701 if (m_initialPlayWithoutUserGestures && m_playing) 772 recordMetricsIfStopping();
702 gesturelessInitialPlayHalted();
703 773
704 if (UserGestureIndicator::processingUserGesture() && m_userGestureRequiredFo rPlay) { 774 if (UserGestureIndicator::processingUserGesture() && m_userGestureRequiredFo rPlay) {
705 recordAutoplayMetric(AutoplayEnabledThroughLoad); 775 recordAutoplayMetric(AutoplayEnabledThroughLoad);
706 m_userGestureRequiredForPlay = false; 776 m_userGestureRequiredForPlay = false;
707 // While usergesture-initiated load()s technically count as autoplayed, 777 // While usergesture-initiated load()s technically count as autoplayed,
708 // they don't feel like such to the users and hence we don't want to 778 // they don't feel like such to the users and hence we don't want to
709 // count them for the purposes of metrics. 779 // count them for the purposes of metrics.
710 m_autoplayMediaCounted = true; 780 m_autoplayMediaCounted = true;
711 } 781 }
712 782
(...skipping 835 matching lines...) Expand 10 before | Expand all | Expand 10 after
1548 scheduleEvent(EventTypeNames::canplay); 1618 scheduleEvent(EventTypeNames::canplay);
1549 if (isPotentiallyPlaying) 1619 if (isPotentiallyPlaying)
1550 scheduleEvent(EventTypeNames::playing); 1620 scheduleEvent(EventTypeNames::playing);
1551 } 1621 }
1552 1622
1553 if (m_autoplaying && m_paused && autoplay()) { 1623 if (m_autoplaying && m_paused && autoplay()) {
1554 autoplayMediaEncountered(); 1624 autoplayMediaEncountered();
1555 1625
1556 if (document().isSandboxed(SandboxAutomaticFeatures)) { 1626 if (document().isSandboxed(SandboxAutomaticFeatures)) {
1557 recordAutoplayMetric(AutoplayDisabledBySandbox); 1627 recordAutoplayMetric(AutoplayDisabledBySandbox);
1558 } else if (!m_userGestureRequiredForPlay) { 1628 } else {
1559 m_paused = false; 1629 // If the autoplay experiment says that it's okay to play now,
1560 invalidateCachedTime(); 1630 // then don't require a user gesture.
1561 scheduleEvent(EventTypeNames::play); 1631 if (autoplayExperimentIsEligible()) {
1562 scheduleEvent(EventTypeNames::playing); 1632 if (autoplayExperimentIsInViewportIfNeeded()) {
1633 autoplayExperimentPrepareToPlay(GesturelessPlaybackStart edByLoad);
1634 // Will play below.
1635 } else {
1636 // Wait for viewport checks to pass.
1637 autoplayExperimentInstallEventListenerIfNeeded();
1638 }
1639 }
1640
1641 if (!m_userGestureRequiredForPlay) {
1642 m_paused = false;
1643 invalidateCachedTime();
1644 scheduleEvent(EventTypeNames::play);
1645 scheduleEvent(EventTypeNames::playing);
1646 }
1563 } 1647 }
1564 } 1648 }
1565 1649
1566 scheduleEvent(EventTypeNames::canplaythrough); 1650 scheduleEvent(EventTypeNames::canplaythrough);
1567 1651
1568 shouldUpdateDisplayState = true; 1652 shouldUpdateDisplayState = true;
1569 } 1653 }
1570 1654
1571 if (shouldUpdateDisplayState) { 1655 if (shouldUpdateDisplayState) {
1572 updateDisplayState(); 1656 updateDisplayState();
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
1953 2037
1954 WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const 2038 WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const
1955 { 2039 {
1956 return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType(); 2040 return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType();
1957 } 2041 }
1958 2042
1959 void HTMLMediaElement::play() 2043 void HTMLMediaElement::play()
1960 { 2044 {
1961 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this); 2045 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this);
1962 2046
2047 // Set the pending state, even if the play isn't going to be pending.
2048 // Eligibility can change if, for example, the mute status changes.
2049 // Having this set is okay.
2050 m_autoplayExperimentPlayPending = true;
2051
1963 if (!UserGestureIndicator::processingUserGesture()) { 2052 if (!UserGestureIndicator::processingUserGesture()) {
1964 autoplayMediaEncountered(); 2053 autoplayMediaEncountered();
2054
2055 if (autoplayExperimentIsEligible()) {
2056 // Remember that m_userGestureRequiredForPlay is required for
2057 // us to be eligible for the experiment.
2058 // If we are able to override the gesture requirement now, then
2059 // do so. Otherwise, install an event listener if we need one.
2060 if (autoplayExperimentIsInViewportIfNeeded()) {
2061 // Override the gesture and play.
2062 autoplayExperimentPrepareToPlay(GesturelessPlaybackStartedByPlay Method);
2063 } else {
2064 // Wait for viewport visibility.
2065 autoplayExperimentInstallEventListenerIfNeeded();
2066 }
2067 }
2068
1965 if (m_userGestureRequiredForPlay) { 2069 if (m_userGestureRequiredForPlay) {
2070 recordAutoplayMetric(PlayMethodFailed);
1966 String message = ExceptionMessages::failedToExecute("play", "HTMLMed iaElement", "API can only be initiated by a user gesture."); 2071 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)); 2072 document().executionContext()->addConsoleMessage(ConsoleMessage::cre ate(JSMessageSource, WarningMessageLevel, message));
1968 return; 2073 return;
1969 } 2074 }
1970 } else if (m_userGestureRequiredForPlay) { 2075 } else if (m_userGestureRequiredForPlay) {
1971 if (m_autoplayMediaCounted) 2076 if (m_autoplayMediaCounted)
1972 recordAutoplayMetric(AutoplayManualStart); 2077 recordAutoplayMetric(AutoplayManualStart);
1973 m_userGestureRequiredForPlay = false; 2078 m_userGestureRequiredForPlay = false;
2079 autoplayExperimentClearEventListenerIfNeeded();
1974 } 2080 }
1975 2081
1976 playInternal(); 2082 playInternal();
1977 } 2083 }
1978 2084
1979 void HTMLMediaElement::playInternal() 2085 void HTMLMediaElement::playInternal()
1980 { 2086 {
1981 WTF_LOG(Media, "HTMLMediaElement::playInternal(%p)", this); 2087 WTF_LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
1982 2088
1983 // 4.8.10.9. Playing the media resource 2089 // 4.8.10.9. Playing the media resource
1984 if (m_networkState == NETWORK_EMPTY) 2090 if (m_networkState == NETWORK_EMPTY)
1985 scheduleDelayedAction(LoadMediaResource); 2091 scheduleDelayedAction(LoadMediaResource);
1986 2092
1987 // Generally "ended" and "looping" are exclusive. Here, the loop attribute 2093 // Generally "ended" and "looping" are exclusive. Here, the loop attribute
1988 // is ignored to seek back to start in case loop was set after playback 2094 // is ignored to seek back to start in case loop was set after playback
1989 // ended. See http://crbug.com/364442 2095 // ended. See http://crbug.com/364442
1990 if (endedPlayback(LoopCondition::Ignored)) 2096 if (endedPlayback(LoopCondition::Ignored))
1991 seek(0); 2097 seek(0);
1992 2098
1993 if (m_mediaController) 2099 if (m_mediaController)
1994 m_mediaController->bringElementUpToSpeed(this); 2100 m_mediaController->bringElementUpToSpeed(this);
1995 2101
1996 if (m_paused) { 2102 if (m_paused) {
1997 m_paused = false; 2103 m_paused = false;
1998 invalidateCachedTime(); 2104 invalidateCachedTime();
1999 scheduleEvent(EventTypeNames::play); 2105 scheduleEvent(EventTypeNames::play);
2106 recordAutoplayMetric(AnyPlaybackStarted);
2000 2107
2001 if (m_readyState <= HAVE_CURRENT_DATA) 2108 if (m_readyState <= HAVE_CURRENT_DATA)
2002 scheduleEvent(EventTypeNames::waiting); 2109 scheduleEvent(EventTypeNames::waiting);
2003 else if (m_readyState >= HAVE_FUTURE_DATA) 2110 else if (m_readyState >= HAVE_FUTURE_DATA)
2004 scheduleEvent(EventTypeNames::playing); 2111 scheduleEvent(EventTypeNames::playing);
2005 } 2112 }
2006 m_autoplaying = false; 2113 m_autoplaying = false;
2007 2114
2008 updatePlayState(); 2115 updatePlayState();
2009 updateMediaController(); 2116 updateMediaController();
2010 } 2117 }
2011 2118
2012 void HTMLMediaElement::autoplayMediaEncountered() 2119 void HTMLMediaElement::autoplayMediaEncountered()
2013 { 2120 {
2014 if (!m_autoplayMediaCounted) { 2121 if (!m_autoplayMediaCounted) {
2015 m_autoplayMediaCounted = true; 2122 m_autoplayMediaCounted = true;
2016 recordAutoplayMetric(AutoplayMediaFound); 2123 recordAutoplayMetric(AutoplayMediaFound);
2017 2124
2018 if (!m_userGestureRequiredForPlay) 2125 if (!m_userGestureRequiredForPlay)
2019 m_initialPlayWithoutUserGestures = true; 2126 m_initialPlayWithoutUserGestures = true;
2020 } 2127 }
2021 } 2128 }
2022 2129
2023 void HTMLMediaElement::gesturelessInitialPlayHalted() 2130 bool HTMLMediaElement::isBailout() const
2024 { 2131 {
2025 ASSERT(m_initialPlayWithoutUserGestures);
2026 m_initialPlayWithoutUserGestures = false;
2027
2028 recordAutoplayMetric(AutoplayStopped);
2029
2030 // We count the user as having bailed-out on the video if they watched 2132 // 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. 2133 // less than one minute and less than 50% of it.
2032 double playedTime = currentTime(); 2134 const double playedTime = currentTime();
2033 if (playedTime < 60) { 2135 const double progress = playedTime / duration();
2034 double progress = playedTime / duration(); 2136 return (playedTime < 60) && (progress < 0.5);
2035 if (progress < 0.5)
2036 recordAutoplayMetric(AutoplayBailout);
2037 }
2038 } 2137 }
2039 2138
2040 void HTMLMediaElement::pause() 2139 void HTMLMediaElement::pause()
2041 { 2140 {
2042 WTF_LOG(Media, "HTMLMediaElement::pause(%p)", this); 2141 WTF_LOG(Media, "HTMLMediaElement::pause(%p)", this);
2043 2142
2044 if (m_networkState == NETWORK_EMPTY) 2143 if (m_networkState == NETWORK_EMPTY)
2045 scheduleDelayedAction(LoadMediaResource); 2144 scheduleDelayedAction(LoadMediaResource);
2046 2145
2146 // Don't try to autoplay, if we would have.
2147 m_autoplayExperimentPlayPending = false;
2148 autoplayExperimentClearEventListenerIfNeeded();
2149
2047 m_autoplaying = false; 2150 m_autoplaying = false;
2048 2151
2049 if (!m_paused) { 2152 if (!m_paused) {
2050 if (m_initialPlayWithoutUserGestures) 2153 recordMetricsIfStopping();
2051 gesturelessInitialPlayHalted();
2052 2154
2053 m_paused = true; 2155 m_paused = true;
2054 scheduleTimeupdateEvent(false); 2156 scheduleTimeupdateEvent(false);
2055 scheduleEvent(EventTypeNames::pause); 2157 scheduleEvent(EventTypeNames::pause);
2056 } 2158 }
2057 2159
2058 updatePlayState(); 2160 updatePlayState();
2059 } 2161 }
2060 2162
2061 void HTMLMediaElement::requestRemotePlayback() 2163 void HTMLMediaElement::requestRemotePlayback()
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
2134 2236
2135 void HTMLMediaElement::setMuted(bool muted) 2237 void HTMLMediaElement::setMuted(bool muted)
2136 { 2238 {
2137 WTF_LOG(Media, "HTMLMediaElement::setMuted(%p, %s)", this, boolString(muted) ); 2239 WTF_LOG(Media, "HTMLMediaElement::setMuted(%p, %s)", this, boolString(muted) );
2138 2240
2139 if (m_muted == muted) 2241 if (m_muted == muted)
2140 return; 2242 return;
2141 2243
2142 m_muted = muted; 2244 m_muted = muted;
2143 2245
2246 // If we are no longer eligible for the autoplay experiment, then also
2247 // quit listening for events. If we are eligible, and if we should be
2248 // playing, then start playing. In other words, start playing if
2249 // we just needed 'mute' to autoplay.
2250 if (!autoplayExperimentIsEligible()) {
2251 autoplayExperimentClearEventListenerIfNeeded();
2252 } else {
2253 // Try to play. If we can't, then install a listener.
2254 if (!autoplayExperimentMaybeStartPlaying())
2255 autoplayExperimentInstallEventListenerIfNeeded();
2256 }
2257
2144 updateVolume(); 2258 updateVolume();
2145 2259
2146 scheduleEvent(EventTypeNames::volumechange); 2260 scheduleEvent(EventTypeNames::volumechange);
2147 } 2261 }
2148 2262
2149 void HTMLMediaElement::updateVolume() 2263 void HTMLMediaElement::updateVolume()
2150 { 2264 {
2151 if (webMediaPlayer()) 2265 if (webMediaPlayer())
2152 webMediaPlayer()->setVolume(effectiveMediaVolume()); 2266 webMediaPlayer()->setVolume(effectiveMediaVolume());
2153 2267
(...skipping 950 matching lines...) Expand 10 before | Expand all | Expand 10 after
3104 mediaControls()->refreshCastButtonVisibility(); 3218 mediaControls()->refreshCastButtonVisibility();
3105 3219
3106 if (layoutObject()) 3220 if (layoutObject())
3107 layoutObject()->setShouldDoFullPaintInvalidation(); 3221 layoutObject()->setShouldDoFullPaintInvalidation();
3108 } 3222 }
3109 3223
3110 void HTMLMediaElement::stop() 3224 void HTMLMediaElement::stop()
3111 { 3225 {
3112 WTF_LOG(Media, "HTMLMediaElement::stop(%p)", this); 3226 WTF_LOG(Media, "HTMLMediaElement::stop(%p)", this);
3113 3227
3114 if (m_playing && m_initialPlayWithoutUserGestures) 3228 recordMetricsIfStopping();
3115 gesturelessInitialPlayHalted();
3116 3229
3117 // Close the async event queue so that no events are enqueued by userCancell edLoad. 3230 // Close the async event queue so that no events are enqueued by userCancell edLoad.
3118 cancelPendingEventsAndCallbacks(); 3231 cancelPendingEventsAndCallbacks();
3119 m_asyncEventQueue->close(); 3232 m_asyncEventQueue->close();
3120 3233
3121 userCancelledLoad(); 3234 userCancelledLoad();
3122 3235
3123 // Stop the playback without generating events 3236 // Stop the playback without generating events
3124 m_playing = false; 3237 m_playing = false;
3125 m_paused = true; 3238 m_paused = true;
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after
3782 { 3895 {
3783 visitor->trace(m_client); 3896 visitor->trace(m_client);
3784 } 3897 }
3785 3898
3786 DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl) 3899 DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl)
3787 { 3900 {
3788 visitor->trace(m_client); 3901 visitor->trace(m_client);
3789 } 3902 }
3790 #endif 3903 #endif
3791 3904
3905 void HTMLMediaElement::autoplayExperimentInstallEventListenerIfNeeded()
3906 {
3907 // If we don't require that the player is in the viewport, then we don't
3908 // need the listener.
3909 if (!(m_autoplayExperimentMode & ExperimentIfViewport))
3910 return;
3911
3912 if (document().domWindow() && !m_autoplayExperimentTouchListener) {
3913 m_autoplayExperimentTouchListener = adoptRef(new AutoplayExperimentTouch Listener(this));
Rick Byers 2015/08/06 15:07:49 nit: please change the names from 'TouchListener'
liberato (no reviews please) 2015/08/06 15:32:27 oops, thanks! was tired, forgot to change the nam
3914 // Listen for events that might show a user-initiated scroll. We
3915 // don't try to catch programmatic scrolls right now.
Rick Byers 2015/08/06 15:07:49 'scroll' fires for programmatic scrolls too. Is t
liberato (no reviews please) 2015/08/06 15:32:27 yeah, stale comment. i really should push CLs out
3916 document().domWindow()->addEventListener("scroll", m_autoplayExperimentT ouchListener, false);
3917 }
3792 } 3918 }
3919
3920 void HTMLMediaElement::autoplayExperimentClearEventListenerIfNeeded()
3921 {
3922 if (m_autoplayExperimentTouchListener) {
3923 LocalDOMWindow* domWindow = document().domWindow();
3924 if (domWindow) {
3925 domWindow->removeEventListener("scroll", m_autoplayExperimentTouchLi stener, false);
3926 }
3927 // Either way, clear our ref.
3928 m_autoplayExperimentTouchListener.clear();
3929 }
3930 }
3931
3932 void HTMLMediaElement::notifyScrolled()
3933 {
3934 // Reset the timer to indicate that scrolling has happened
3935 // recently, and might still be ongoing.
3936 m_autoplayViewportTimer.startOneShot(viewportTimerPollDelay, FROM_HERE);
3937 }
3938
3939 void HTMLMediaElement::viewportTimerFired(Timer<HTMLMediaElement>*)
3940 {
3941 // Sufficient time has passed since the last scroll that we'll
3942 // treat it as the end of scroll. Autoplay if we should.
3943 autoplayExperimentMaybeStartPlaying();
3944 }
3945
3946 bool HTMLMediaElement::autoplayExperimentIsInViewportIfNeeded()
3947 {
3948 // We could check for eligibility here, but we skip it. Some of our
3949 // callers need to do it separately, and we don't want to check more
3950 // than we need to.
3951 // Also remember that page visibility is assumed for clank.
3952
3953 // If viewport visibility isn't required, then it's visible enough.
3954 if (!(m_autoplayExperimentMode & ExperimentIfViewport))
3955 return true;
3956
3957 // Check if we're in the viewport.
3958 const LocalDOMWindow* domWindow = document().domWindow();
3959 if (!domWindow)
3960 return false;
3961
3962 FloatRect us(offsetLeft(), offsetTop(), clientWidth(), clientHeight());
3963 FloatRect screen(domWindow->scrollX(), domWindow->scrollY(), domWindow->inne rWidth(), domWindow->innerHeight());
3964
3965 return screen.contains(us);
3966 }
3967
3968 bool HTMLMediaElement::autoplayExperimentMaybeStartPlaying()
3969 {
3970 if (!autoplayExperimentIsEligible()
3971 || !autoplayExperimentIsInViewportIfNeeded()) {
3972 return false;
3973 }
3974
3975 // Start playing!
3976 autoplayExperimentPrepareToPlay(GesturelessPlaybackStartedByScroll);
3977 playInternal();
3978
3979 return true;
3980 }
3981
3982 bool HTMLMediaElement::autoplayExperimentIsEligible() const
3983 {
3984 // If no user gesture is required, then the experiment doesn't apply.
3985 // This is what prevents us from starting playback more than once.
3986 // Since this flag is never set to true once it's cleared, it will block
3987 // the autoplay experiment forever.
3988 if (!m_userGestureRequiredForPlay)
3989 return false;
3990
3991 if (m_autoplayExperimentMode == ExperimentOff)
3992 return false;
3993
3994 // If nobody has requested playback, either by the autoplay attribute or
3995 // a play() call, then do nothing.
3996 if (!m_autoplayExperimentPlayPending && !autoplay())
3997 return false;
3998
3999 // If the video is already playing, then do nothing. Note that there
4000 // is not a path where a user gesture is required but the video is
4001 // playing. However, we check for completeness.
4002 if (!m_paused)
4003 return false;
4004
4005 // Note that the viewport test always returns false on desktop, which is
4006 // why video-autoplay-experiment.html doesn't check -ifmobile .
4007 if (m_autoplayExperimentMode & ExperimentIfMobile) {
4008 if (!document().viewportDescription().isLegacyViewportType())
4009 return false;
4010 }
4011
4012 if (m_autoplayExperimentMode & ExperimentIfMuted) {
4013 // If media is muted, then autoplay when it comes into view.
4014 return fastHasAttribute(mutedAttr) || m_muted;
4015 }
4016
4017 // Autoplay when it comes into view (if needed), maybe muted.
4018 return true;
4019 }
4020
4021 void HTMLMediaElement::autoplayExperimentMuteIfNeeded()
4022 {
4023 if (m_autoplayExperimentMode & ExperimentPlayMuted) {
4024 m_muted = true;
4025 updateVolume();
4026 }
4027 }
4028
4029 void HTMLMediaElement::autoplayExperimentPrepareToPlay(AutoplayMetrics metric)
4030 {
4031 recordAutoplayMetric(metric);
4032
4033 // This also causes !autoplayExperimentIsEligible, so that we don't
4034 // allow autoplay more than once.
4035 m_userGestureRequiredForPlay = false;
4036
4037 m_autoplayExperimentStartedByExperiment = true;
4038 autoplayExperimentClearEventListenerIfNeeded();
4039 autoplayExperimentMuteIfNeeded();
4040
4041 // Record that this autoplayed without a user gesture. This is normally
4042 // set when we discover an autoplay attribute, but we include all cases
4043 // where playback started without a user gesture, e.g., play().
4044 m_initialPlayWithoutUserGestures = true;
4045 }
4046
4047 }
OLDNEW
« no previous file with comments | « Source/core/html/HTMLMediaElement.h ('k') | Source/core/testing/Internals.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698