Chromium Code Reviews| 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 |