OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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::AutoplayExperimentScrollListener : public EventListener { | |
dglazkov
2015/08/06 20:55:23
Is this the right approach? Adding Ojan, the TL fo
liberato (no reviews please)
2015/09/01 06:54:19
Done.
| |
290 public: | |
291 AutoplayExperimentScrollListener(HTMLMediaElement* element) : EventListe ner(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 Loading... | |
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")) { | |
dglazkov
2015/08/06 20:55:23
See my note above about not using strings for pass
| |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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_autoplayExperimentScrollListener) { | |
3913 m_autoplayExperimentScrollListener = adoptRef(new AutoplayExperimentScro llListener(this)); | |
3914 document().domWindow()->addEventListener("scroll", m_autoplayExperimentS crollListener, false); | |
dglazkov
2015/08/06 20:55:23
Ojan, PTAL.
| |
3915 } | |
3792 } | 3916 } |
3917 | |
3918 void HTMLMediaElement::autoplayExperimentClearEventListenerIfNeeded() | |
3919 { | |
3920 if (m_autoplayExperimentScrollListener) { | |
3921 LocalDOMWindow* domWindow = document().domWindow(); | |
3922 if (domWindow) { | |
3923 domWindow->removeEventListener("scroll", m_autoplayExperimentScrollL istener, false); | |
3924 } | |
3925 // Either way, clear our ref. | |
3926 m_autoplayExperimentScrollListener.clear(); | |
3927 } | |
3928 } | |
3929 | |
3930 void HTMLMediaElement::notifyScrolled() | |
3931 { | |
3932 // Reset the timer to indicate that scrolling has happened | |
3933 // recently, and might still be ongoing. | |
3934 m_autoplayViewportTimer.startOneShot(viewportTimerPollDelay, FROM_HERE); | |
3935 } | |
3936 | |
3937 void HTMLMediaElement::viewportTimerFired(Timer<HTMLMediaElement>*) | |
3938 { | |
3939 // Sufficient time has passed since the last scroll that we'll | |
3940 // treat it as the end of scroll. Autoplay if we should. | |
3941 autoplayExperimentMaybeStartPlaying(); | |
3942 } | |
3943 | |
3944 bool HTMLMediaElement::autoplayExperimentIsInViewportIfNeeded() | |
3945 { | |
3946 // We could check for eligibility here, but we skip it. Some of our | |
3947 // callers need to do it separately, and we don't want to check more | |
3948 // than we need to. | |
3949 // Also remember that page visibility is assumed for clank. | |
3950 | |
3951 // If viewport visibility isn't required, then it's visible enough. | |
3952 if (!(m_autoplayExperimentMode & ExperimentIfViewport)) | |
3953 return true; | |
3954 | |
3955 // Check if we're in the viewport. | |
3956 const LocalDOMWindow* domWindow = document().domWindow(); | |
3957 if (!domWindow) | |
3958 return false; | |
3959 | |
3960 FloatRect us(offsetLeft(), offsetTop(), clientWidth(), clientHeight()); | |
dglazkov
2015/08/06 20:55:23
This will force layout, and something we should av
liberato (no reviews please)
2015/08/07 14:33:15
one alternative is checking layoutObject()->needsL
| |
3961 FloatRect screen(domWindow->scrollX(), domWindow->scrollY(), domWindow->inne rWidth(), domWindow->innerHeight()); | |
3962 | |
3963 return screen.contains(us); | |
3964 } | |
3965 | |
3966 bool HTMLMediaElement::autoplayExperimentMaybeStartPlaying() | |
3967 { | |
3968 if (!autoplayExperimentIsEligible() | |
3969 || !autoplayExperimentIsInViewportIfNeeded()) { | |
3970 return false; | |
3971 } | |
3972 | |
3973 // Start playing! | |
3974 autoplayExperimentPrepareToPlay(GesturelessPlaybackStartedByScroll); | |
3975 playInternal(); | |
3976 | |
3977 return true; | |
3978 } | |
3979 | |
3980 bool HTMLMediaElement::autoplayExperimentIsEligible() const | |
3981 { | |
3982 // If no user gesture is required, then the experiment doesn't apply. | |
3983 // This is what prevents us from starting playback more than once. | |
3984 // Since this flag is never set to true once it's cleared, it will block | |
3985 // the autoplay experiment forever. | |
3986 if (!m_userGestureRequiredForPlay) | |
3987 return false; | |
3988 | |
3989 if (m_autoplayExperimentMode == ExperimentOff) | |
3990 return false; | |
3991 | |
3992 // If nobody has requested playback, either by the autoplay attribute or | |
3993 // a play() call, then do nothing. | |
3994 if (!m_autoplayExperimentPlayPending && !autoplay()) | |
3995 return false; | |
3996 | |
3997 // If the video is already playing, then do nothing. Note that there | |
3998 // is not a path where a user gesture is required but the video is | |
3999 // playing. However, we check for completeness. | |
4000 if (!m_paused) | |
4001 return false; | |
4002 | |
4003 // Note that the viewport test always returns false on desktop, which is | |
4004 // why video-autoplay-experiment.html doesn't check -ifmobile . | |
4005 if (m_autoplayExperimentMode & ExperimentIfMobile) { | |
4006 if (!document().viewportDescription().isLegacyViewportType()) | |
4007 return false; | |
4008 } | |
4009 | |
4010 if (m_autoplayExperimentMode & ExperimentIfMuted) { | |
4011 // If media is muted, then autoplay when it comes into view. | |
4012 return fastHasAttribute(mutedAttr) || m_muted; | |
4013 } | |
4014 | |
4015 // Autoplay when it comes into view (if needed), maybe muted. | |
4016 return true; | |
4017 } | |
4018 | |
4019 void HTMLMediaElement::autoplayExperimentMuteIfNeeded() | |
4020 { | |
4021 if (m_autoplayExperimentMode & ExperimentPlayMuted) { | |
4022 m_muted = true; | |
4023 updateVolume(); | |
4024 } | |
4025 } | |
4026 | |
4027 void HTMLMediaElement::autoplayExperimentPrepareToPlay(AutoplayMetrics metric) | |
4028 { | |
4029 recordAutoplayMetric(metric); | |
4030 | |
4031 // This also causes !autoplayExperimentIsEligible, so that we don't | |
4032 // allow autoplay more than once. | |
4033 m_userGestureRequiredForPlay = false; | |
4034 | |
4035 m_autoplayExperimentStartedByExperiment = true; | |
4036 autoplayExperimentClearEventListenerIfNeeded(); | |
4037 autoplayExperimentMuteIfNeeded(); | |
4038 | |
4039 // Record that this autoplayed without a user gesture. This is normally | |
4040 // set when we discover an autoplay attribute, but we include all cases | |
4041 // where playback started without a user gesture, e.g., play(). | |
4042 m_initialPlayWithoutUserGestures = true; | |
4043 } | |
4044 | |
4045 } | |
OLD | NEW |