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 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::AutoplayExperimentTouchListener : public EventListener { | |
| 290 public: | |
| 291 AutoplayExperimentTouchListener(HTMLMediaElement* element) : EventListen er(CPPEventListenerType), m_element(element) { } | |
| 292 virtual bool operator==(const EventListener& them) | |
| 293 { | |
| 294 return &them == this; | |
| 295 } | |
| 296 | |
| 297 void handleEvent(ExecutionContext*, Event*) override | |
| 298 { | |
| 299 if (m_element) | |
| 300 m_element->notifyScrolled(); | |
| 301 } | |
| 302 | |
| 303 private: | |
| 304 HTMLMediaElement* m_element; | |
| 305 }; | |
| 306 | |
| 303 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry) | 307 void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry) |
| 304 { | 308 { |
| 305 ASSERT(!s_mediaStreamRegistry); | 309 ASSERT(!s_mediaStreamRegistry); |
| 306 s_mediaStreamRegistry = registry; | 310 s_mediaStreamRegistry = registry; |
| 307 } | 311 } |
| 308 | 312 |
| 309 bool HTMLMediaElement::isMediaStreamURL(const String& url) | 313 bool HTMLMediaElement::isMediaStreamURL(const String& url) |
| 310 { | 314 { |
| 311 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false; | 315 return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false; |
| 312 } | 316 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after 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")) { | |
| 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_autoplayExperimentTouchListener) { | |
| 3913 m_autoplayExperimentTouchListener = adoptRef(new AutoplayExperimentTouch Listener(this)); | |
|
Rick Byers
2015/08/06 15:07:49
nit: please change the names from 'TouchListener'
liberato (no reviews please)
2015/08/06 15:32:27
oops, thanks! was tired, forgot to change the nam
| |
| 3914 // Listen for events that might show a user-initiated scroll. We | |
| 3915 // don't try to catch programmatic scrolls right now. | |
|
Rick Byers
2015/08/06 15:07:49
'scroll' fires for programmatic scrolls too. Is t
liberato (no reviews please)
2015/08/06 15:32:27
yeah, stale comment. i really should push CLs out
| |
| 3916 document().domWindow()->addEventListener("scroll", m_autoplayExperimentT ouchListener, false); | |
| 3917 } | |
| 3792 } | 3918 } |
| 3919 | |
| 3920 void HTMLMediaElement::autoplayExperimentClearEventListenerIfNeeded() | |
| 3921 { | |
| 3922 if (m_autoplayExperimentTouchListener) { | |
| 3923 LocalDOMWindow* domWindow = document().domWindow(); | |
| 3924 if (domWindow) { | |
| 3925 domWindow->removeEventListener("scroll", m_autoplayExperimentTouchLi stener, false); | |
| 3926 } | |
| 3927 // Either way, clear our ref. | |
| 3928 m_autoplayExperimentTouchListener.clear(); | |
| 3929 } | |
| 3930 } | |
| 3931 | |
| 3932 void HTMLMediaElement::notifyScrolled() | |
| 3933 { | |
| 3934 // Reset the timer to indicate that scrolling has happened | |
| 3935 // recently, and might still be ongoing. | |
| 3936 m_autoplayViewportTimer.startOneShot(viewportTimerPollDelay, FROM_HERE); | |
| 3937 } | |
| 3938 | |
| 3939 void HTMLMediaElement::viewportTimerFired(Timer<HTMLMediaElement>*) | |
| 3940 { | |
| 3941 // Sufficient time has passed since the last scroll that we'll | |
| 3942 // treat it as the end of scroll. Autoplay if we should. | |
| 3943 autoplayExperimentMaybeStartPlaying(); | |
| 3944 } | |
| 3945 | |
| 3946 bool HTMLMediaElement::autoplayExperimentIsInViewportIfNeeded() | |
| 3947 { | |
| 3948 // We could check for eligibility here, but we skip it. Some of our | |
| 3949 // callers need to do it separately, and we don't want to check more | |
| 3950 // than we need to. | |
| 3951 // Also remember that page visibility is assumed for clank. | |
| 3952 | |
| 3953 // If viewport visibility isn't required, then it's visible enough. | |
| 3954 if (!(m_autoplayExperimentMode & ExperimentIfViewport)) | |
| 3955 return true; | |
| 3956 | |
| 3957 // Check if we're in the viewport. | |
| 3958 const LocalDOMWindow* domWindow = document().domWindow(); | |
| 3959 if (!domWindow) | |
| 3960 return false; | |
| 3961 | |
| 3962 FloatRect us(offsetLeft(), offsetTop(), clientWidth(), clientHeight()); | |
| 3963 FloatRect screen(domWindow->scrollX(), domWindow->scrollY(), domWindow->inne rWidth(), domWindow->innerHeight()); | |
| 3964 | |
| 3965 return screen.contains(us); | |
| 3966 } | |
| 3967 | |
| 3968 bool HTMLMediaElement::autoplayExperimentMaybeStartPlaying() | |
| 3969 { | |
| 3970 if (!autoplayExperimentIsEligible() | |
| 3971 || !autoplayExperimentIsInViewportIfNeeded()) { | |
| 3972 return false; | |
| 3973 } | |
| 3974 | |
| 3975 // Start playing! | |
| 3976 autoplayExperimentPrepareToPlay(GesturelessPlaybackStartedByScroll); | |
| 3977 playInternal(); | |
| 3978 | |
| 3979 return true; | |
| 3980 } | |
| 3981 | |
| 3982 bool HTMLMediaElement::autoplayExperimentIsEligible() const | |
| 3983 { | |
| 3984 // If no user gesture is required, then the experiment doesn't apply. | |
| 3985 // This is what prevents us from starting playback more than once. | |
| 3986 // Since this flag is never set to true once it's cleared, it will block | |
| 3987 // the autoplay experiment forever. | |
| 3988 if (!m_userGestureRequiredForPlay) | |
| 3989 return false; | |
| 3990 | |
| 3991 if (m_autoplayExperimentMode == ExperimentOff) | |
| 3992 return false; | |
| 3993 | |
| 3994 // If nobody has requested playback, either by the autoplay attribute or | |
| 3995 // a play() call, then do nothing. | |
| 3996 if (!m_autoplayExperimentPlayPending && !autoplay()) | |
| 3997 return false; | |
| 3998 | |
| 3999 // If the video is already playing, then do nothing. Note that there | |
| 4000 // is not a path where a user gesture is required but the video is | |
| 4001 // playing. However, we check for completeness. | |
| 4002 if (!m_paused) | |
| 4003 return false; | |
| 4004 | |
| 4005 // Note that the viewport test always returns false on desktop, which is | |
| 4006 // why video-autoplay-experiment.html doesn't check -ifmobile . | |
| 4007 if (m_autoplayExperimentMode & ExperimentIfMobile) { | |
| 4008 if (!document().viewportDescription().isLegacyViewportType()) | |
| 4009 return false; | |
| 4010 } | |
| 4011 | |
| 4012 if (m_autoplayExperimentMode & ExperimentIfMuted) { | |
| 4013 // If media is muted, then autoplay when it comes into view. | |
| 4014 return fastHasAttribute(mutedAttr) || m_muted; | |
| 4015 } | |
| 4016 | |
| 4017 // Autoplay when it comes into view (if needed), maybe muted. | |
| 4018 return true; | |
| 4019 } | |
| 4020 | |
| 4021 void HTMLMediaElement::autoplayExperimentMuteIfNeeded() | |
| 4022 { | |
| 4023 if (m_autoplayExperimentMode & ExperimentPlayMuted) { | |
| 4024 m_muted = true; | |
| 4025 updateVolume(); | |
| 4026 } | |
| 4027 } | |
| 4028 | |
| 4029 void HTMLMediaElement::autoplayExperimentPrepareToPlay(AutoplayMetrics metric) | |
| 4030 { | |
| 4031 recordAutoplayMetric(metric); | |
| 4032 | |
| 4033 // This also causes !autoplayExperimentIsEligible, so that we don't | |
| 4034 // allow autoplay more than once. | |
| 4035 m_userGestureRequiredForPlay = false; | |
| 4036 | |
| 4037 m_autoplayExperimentStartedByExperiment = true; | |
| 4038 autoplayExperimentClearEventListenerIfNeeded(); | |
| 4039 autoplayExperimentMuteIfNeeded(); | |
| 4040 | |
| 4041 // Record that this autoplayed without a user gesture. This is normally | |
| 4042 // set when we discover an autoplay attribute, but we include all cases | |
| 4043 // where playback started without a user gesture, e.g., play(). | |
| 4044 m_initialPlayWithoutUserGestures = true; | |
| 4045 } | |
| 4046 | |
| 4047 } | |
| OLD | NEW |