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