| Index: third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| index 6dcd96d4480275f0b01cedc35b52bcee28b1e437..65d6a79d225ae1e53d0d1842c3d19465ac014e6c 100644
|
| --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| @@ -37,6 +37,7 @@
|
| #include "core/dom/Fullscreen.h"
|
| #include "core/dom/shadow/ShadowRoot.h"
|
| #include "core/events/Event.h"
|
| +#include "core/frame/FrameView.h"
|
| #include "core/frame/LocalFrame.h"
|
| #include "core/frame/Settings.h"
|
| #include "core/frame/UseCounter.h"
|
| @@ -60,6 +61,7 @@
|
| #include "core/inspector/ConsoleMessage.h"
|
| #include "core/layout/LayoutVideo.h"
|
| #include "core/layout/LayoutView.h"
|
| +#include "core/layout/api/LayoutMediaItem.h"
|
| #include "core/layout/compositing/PaintLayerCompositor.h"
|
| #include "core/loader/FrameLoader.h"
|
| #include "core/loader/FrameLoaderClient.h"
|
| @@ -269,6 +271,57 @@ String preloadTypeToString(WebMediaPlayer::Preload preloadType)
|
|
|
| } // anonymous namespace
|
|
|
| +class HTMLMediaElement::AutoplayHelperClientImpl :
|
| + public AutoplayExperimentHelper::Client {
|
| +
|
| +public:
|
| + static PassOwnPtrWillBeRawPtr<AutoplayHelperClientImpl> create(HTMLMediaElement* element)
|
| + {
|
| + return adoptPtrWillBeNoop(new AutoplayHelperClientImpl(element));
|
| + }
|
| +
|
| + virtual ~AutoplayHelperClientImpl();
|
| +
|
| + using RecordMetricsBehavior = HTMLMediaElement::RecordMetricsBehavior;
|
| +
|
| + double currentTime() const override { return m_element->currentTime(); }
|
| + double duration() const override { return m_element->duration(); }
|
| + bool ended() const override { return m_element->ended(); }
|
| + bool muted() const override { return m_element->muted(); }
|
| + void setMuted(bool muted) override { m_element->setMuted(muted); }
|
| + void playInternal() override { m_element->playInternal(); }
|
| + bool isUserGestureRequiredForPlay() const override { return m_element->isUserGestureRequiredForPlay(); }
|
| + void removeUserGestureRequirement() override { m_element->removeUserGestureRequirement(); }
|
| + void recordAutoplayMetric(AutoplayMetrics metric) override { m_element->recordAutoplayMetric(metric); }
|
| + bool shouldAutoplay() override
|
| + {
|
| + return m_element->shouldAutoplay(RecordMetricsBehavior::DoNotRecord);
|
| + }
|
| + bool isHTMLVideoElement() const override { return m_element->isHTMLVideoElement(); }
|
| + bool isHTMLAudioElement() const override { return m_element->isHTMLAudioElement(); }
|
| +
|
| + // Document
|
| + bool isLegacyViewportType() override;
|
| + PageVisibilityState pageVisibilityState() const override;
|
| + String autoplayExperimentMode() const override;
|
| +
|
| + // LayoutObject
|
| + void setRequestPositionUpdates(bool) override;
|
| + IntRect absoluteBoundingBoxRect() const override;
|
| +
|
| + DEFINE_INLINE_VIRTUAL_TRACE()
|
| + {
|
| + visitor->trace(m_element);
|
| + Client::trace(visitor);
|
| + }
|
| +
|
| +private:
|
| + AutoplayHelperClientImpl(HTMLMediaElement* element) : m_element(element) {}
|
| +
|
| + RawPtrWillBeMember<HTMLMediaElement> m_element;
|
| +};
|
| +
|
| +
|
| void HTMLMediaElement::recordAutoplayMetric(AutoplayMetrics metric)
|
| {
|
| DEFINE_STATIC_LOCAL(EnumerationHistogram, autoplayHistogram, ("Blink.MediaElement.Autoplay", NumberOfAutoplayMetrics));
|
| @@ -355,8 +408,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
|
| , m_remoteRoutesAvailable(false)
|
| , m_playingRemotely(false)
|
| , m_isFinalizing(false)
|
| - , m_initialPlayWithoutUserGesture(false)
|
| - , m_autoplayMediaCounted(false)
|
| , m_inOverlayFullscreenVideo(false)
|
| , m_audioTracks(AudioTrackList::create(*this))
|
| , m_videoTracks(VideoTrackList::create(*this))
|
| @@ -364,7 +415,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
|
| , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaElement::resolvePlayPromises))
|
| , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaElement::rejectPlayPromises))
|
| , m_audioSourceNode(nullptr)
|
| - , m_autoplayHelper(*this)
|
| + , m_autoplayHelperClient(AutoplayHelperClientImpl::create(this))
|
| + , m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.get()))
|
| , m_remotePlaybackClient(nullptr)
|
| {
|
| #if ENABLE(OILPAN)
|
| @@ -387,6 +439,11 @@ HTMLMediaElement::~HTMLMediaElement()
|
| WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this);
|
|
|
| #if !ENABLE(OILPAN)
|
| + // Destruction of the autoplay helper requires the client, so be sure that
|
| + // this happens before the client is destructed.
|
| + if (m_autoplayHelper)
|
| + m_autoplayHelper.clear();
|
| +
|
| // HTMLMediaElement and m_asyncEventQueue always become unreachable
|
| // together. So HTMLMediaElement and m_asyncEventQueue are destructed in
|
| // the same GC. We don't need to close it explicitly in Oilpan.
|
| @@ -426,6 +483,10 @@ HTMLMediaElement::~HTMLMediaElement()
|
| #if ENABLE(OILPAN)
|
| void HTMLMediaElement::dispose()
|
| {
|
| + // This must happen before we're destructed.
|
| + if (m_autoplayHelper)
|
| + m_autoplayHelper->dispose();
|
| +
|
| // If the HTMLMediaElement dies with the Document we are not
|
| // allowed to touch the Document to adjust delay load event counts
|
| // from the destructor, as the Document could have been already
|
| @@ -682,47 +743,11 @@ String HTMLMediaElement::canPlayType(const String& mimeType) const
|
| return canPlay;
|
| }
|
|
|
| -void HTMLMediaElement::recordMetricsIfPausing()
|
| -{
|
| - // If not playing, then nothing to record.
|
| - // TODO(liberato): test metrics. this was m_paused.
|
| - if (m_paused)
|
| - return;
|
| -
|
| - const bool bailout = isBailout();
|
| -
|
| - // Record that play was paused. We don't care if it was autoplay,
|
| - // play(), or the user manually started it.
|
| - recordAutoplayMetric(AnyPlaybackPaused);
|
| - if (bailout)
|
| - recordAutoplayMetric(AnyPlaybackBailout);
|
| -
|
| - // If this was a gestureless play, then record that separately.
|
| - // These cover attr and play() gestureless starts.
|
| - if (m_initialPlayWithoutUserGesture) {
|
| - m_initialPlayWithoutUserGesture = false;
|
| -
|
| - recordAutoplayMetric(AutoplayPaused);
|
| -
|
| - if (bailout)
|
| - recordAutoplayMetric(AutoplayBailout);
|
| - }
|
| -}
|
| -
|
| void HTMLMediaElement::load()
|
| {
|
| WTF_LOG(Media, "HTMLMediaElement::load(%p)", this);
|
|
|
| - recordMetricsIfPausing();
|
| -
|
| - if (UserGestureIndicator::processingUserGesture() && m_userGestureRequiredForPlay) {
|
| - recordAutoplayMetric(AutoplayEnabledThroughLoad);
|
| - m_userGestureRequiredForPlay = false;
|
| - // While usergesture-initiated load()s technically count as autoplayed,
|
| - // they don't feel like such to the users and hence we don't want to
|
| - // count them for the purposes of metrics.
|
| - m_autoplayMediaCounted = true;
|
| - }
|
| + m_autoplayHelper->loadMethodCalled();
|
|
|
| m_ignorePreloadNone = true;
|
| invokeLoadAlgorithm();
|
| @@ -964,6 +989,8 @@ void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType)
|
| // The resource fetch algorithm
|
| setNetworkState(NETWORK_LOADING);
|
|
|
| + m_autoplayHelper->loadingStarted();
|
| +
|
| // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
|
| // cache is an internal detail not exposed through the media element API.
|
| m_currentSrc = url;
|
| @@ -990,7 +1017,7 @@ void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType)
|
|
|
| if (url.protocolIs(mediaSourceBlobProtocol)) {
|
| if (isMediaStreamURL(url.getString())) {
|
| - m_userGestureRequiredForPlay = false;
|
| + m_autoplayHelper->removeUserGestureRequirement(GesturelessPlaybackEnabledByStream);
|
| } else {
|
| m_mediaSource = HTMLMediaSource::lookup(url.getString());
|
|
|
| @@ -1579,7 +1606,7 @@ void HTMLMediaElement::setReadyState(ReadyState state)
|
| if (shouldAutoplay(RecordMetricsBehavior::DoRecord)) {
|
| // If the autoplay experiment says that it's okay to play now,
|
| // then don't require a user gesture.
|
| - m_autoplayHelper.becameReadyToPlay();
|
| + m_autoplayHelper->becameReadyToPlay();
|
|
|
| if (!m_userGestureRequiredForPlay) {
|
| m_paused = false;
|
| @@ -1713,8 +1740,6 @@ void HTMLMediaElement::seek(double time)
|
| // 11 - Set the current playback position to the given new playback position.
|
| webMediaPlayer()->seek(time);
|
|
|
| - m_initialPlayWithoutUserGesture = false;
|
| -
|
| // 14-17 are handled, if necessary, when the engine signals a readystate change or otherwise
|
| // satisfies seek completion and signals a time change.
|
| }
|
| @@ -1898,12 +1923,9 @@ bool HTMLMediaElement::autoplay() const
|
| bool HTMLMediaElement::shouldAutoplay(const RecordMetricsBehavior recordMetrics)
|
| {
|
| if (m_autoplaying && m_paused && autoplay()) {
|
| - if (recordMetrics == RecordMetricsBehavior::DoRecord)
|
| - autoplayMediaEncountered();
|
| -
|
| if (document().isSandboxed(SandboxAutomaticFeatures)) {
|
| if (recordMetrics == RecordMetricsBehavior::DoRecord)
|
| - recordAutoplayMetric(AutoplayDisabledBySandbox);
|
| + m_autoplayHelper->recordSandboxFailure();
|
| return false;
|
| }
|
|
|
| @@ -2005,11 +2027,9 @@ Nullable<ExceptionCode> HTMLMediaElement::play()
|
| {
|
| WTF_LOG(Media, "HTMLMediaElement::play(%p)", this);
|
|
|
| - m_autoplayHelper.playMethodCalled();
|
| + m_autoplayHelper->playMethodCalled();
|
|
|
| if (!UserGestureIndicator::processingUserGesture()) {
|
| - autoplayMediaEncountered();
|
| -
|
| if (m_userGestureRequiredForPlay) {
|
| recordAutoplayMetric(PlayMethodFailed);
|
| String message = ExceptionMessages::failedToExecute("play", "HTMLMediaElement", "API can only be initiated by a user gesture.");
|
| @@ -2017,12 +2037,10 @@ Nullable<ExceptionCode> HTMLMediaElement::play()
|
| return NotAllowedError;
|
| }
|
| } else {
|
| + // We ask the helper to remove the gesture requirement for us, so that
|
| + // it can record the reason.
|
| Platform::current()->recordAction(UserMetricsAction("Media_Play_WithGesture"));
|
| - if (m_userGestureRequiredForPlay) {
|
| - if (m_autoplayMediaCounted)
|
| - recordAutoplayMetric(AutoplayManualStart);
|
| - m_userGestureRequiredForPlay = false;
|
| - }
|
| + m_autoplayHelper->removeUserGestureRequirement(GesturelessPlaybackEnabledByPlayMethod);
|
| }
|
|
|
| if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED)
|
| @@ -2072,26 +2090,6 @@ void HTMLMediaElement::playInternal()
|
| updatePlayState();
|
| }
|
|
|
| -void HTMLMediaElement::autoplayMediaEncountered()
|
| -{
|
| - if (!m_autoplayMediaCounted) {
|
| - m_autoplayMediaCounted = true;
|
| - recordAutoplayMetric(AutoplayMediaFound);
|
| -
|
| - if (!m_userGestureRequiredForPlay)
|
| - m_initialPlayWithoutUserGesture = true;
|
| - }
|
| -}
|
| -
|
| -bool HTMLMediaElement::isBailout() const
|
| -{
|
| - // We count the user as having bailed-out on the video if they watched
|
| - // less than one minute and less than 50% of it.
|
| - const double playedTime = currentTime();
|
| - const double progress = playedTime / duration();
|
| - return (playedTime < 60) && (progress < 0.5);
|
| -}
|
| -
|
| void HTMLMediaElement::pause()
|
| {
|
| WTF_LOG(Media, "HTMLMediaElement::pause(%p)", this);
|
| @@ -2111,13 +2109,11 @@ void HTMLMediaElement::pauseInternal()
|
| if (m_networkState == NETWORK_EMPTY)
|
| invokeResourceSelectionAlgorithm();
|
|
|
| - m_autoplayHelper.pauseMethodCalled();
|
| + m_autoplayHelper->pauseMethodCalled();
|
|
|
| m_autoplaying = false;
|
|
|
| if (!m_paused) {
|
| - recordMetricsIfPausing();
|
| -
|
| m_paused = true;
|
| scheduleTimeupdateEvent(false);
|
| scheduleEvent(EventTypeNames::pause);
|
| @@ -2227,7 +2223,7 @@ void HTMLMediaElement::setMuted(bool muted)
|
|
|
| m_muted = muted;
|
|
|
| - m_autoplayHelper.mutedChanged();
|
| + m_autoplayHelper->mutedChanged();
|
|
|
| updateVolume();
|
|
|
| @@ -2851,7 +2847,6 @@ void HTMLMediaElement::timeChanged()
|
| m_sentEndEvent = true;
|
| scheduleEvent(EventTypeNames::ended);
|
| }
|
| - recordMetricsIfPausing();
|
| Platform::current()->recordAction(UserMetricsAction("Media_Playback_Ended"));
|
| }
|
| } else {
|
| @@ -3067,18 +3062,19 @@ void HTMLMediaElement::updatePlayState()
|
| webMediaPlayer()->play();
|
| Platform::current()->recordAction(
|
| UserMetricsAction("Media_Playback_Started"));
|
| + m_autoplayHelper->playbackStarted();
|
| }
|
|
|
| if (mediaControls())
|
| mediaControls()->playbackStarted();
|
| startPlaybackProgressTimer();
|
| m_playing = true;
|
| - recordAutoplayMetric(AnyPlaybackStarted);
|
|
|
| } else { // Should not be playing right now
|
| if (isPlaying) {
|
| webMediaPlayer()->pause();
|
| Platform::current()->recordAction(UserMetricsAction("Media_Paused"));
|
| + m_autoplayHelper->playbackStopped();
|
| }
|
|
|
| refreshCachedTime();
|
| @@ -3145,8 +3141,6 @@ void HTMLMediaElement::stop()
|
| {
|
| WTF_LOG(Media, "HTMLMediaElement::stop(%p)", this);
|
|
|
| - recordMetricsIfPausing();
|
| -
|
| // Close the async event queue so that no events are enqueued.
|
| cancelPendingEventsAndCallbacks();
|
| m_asyncEventQueue->close();
|
| @@ -3621,8 +3615,9 @@ DEFINE_TRACE(HTMLMediaElement)
|
| visitor->trace(m_textTracksWhenResourceSelectionBegan);
|
| visitor->trace(m_playResolvers);
|
| visitor->trace(m_audioSourceProvider);
|
| - visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::clearWeakMembers>(this);
|
| + visitor->trace(m_autoplayHelperClient);
|
| visitor->trace(m_autoplayHelper);
|
| + visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::clearWeakMembers>(this);
|
| HeapSupplementable<HTMLMediaElement>::trace(visitor);
|
| #endif
|
| HTMLElement::trace(visitor);
|
| @@ -3667,11 +3662,6 @@ void HTMLMediaElement::removeUserGestureRequirement()
|
| m_userGestureRequiredForPlay = false;
|
| }
|
|
|
| -void HTMLMediaElement::setInitialPlayWithoutUserGestures(bool value)
|
| -{
|
| - m_initialPlayWithoutUserGesture = value;
|
| -}
|
| -
|
| void HTMLMediaElement::setNetworkState(NetworkState state)
|
| {
|
| if (m_networkState != state) {
|
| @@ -3683,12 +3673,12 @@ void HTMLMediaElement::setNetworkState(NetworkState state)
|
|
|
| void HTMLMediaElement::notifyPositionMayHaveChanged(const IntRect& visibleRect)
|
| {
|
| - m_autoplayHelper.positionChanged(visibleRect);
|
| + m_autoplayHelper->positionChanged(visibleRect);
|
| }
|
|
|
| void HTMLMediaElement::updatePositionNotificationRegistration()
|
| {
|
| - m_autoplayHelper.updatePositionNotificationRegistration();
|
| + m_autoplayHelper->updatePositionNotificationRegistration();
|
| }
|
|
|
| void HTMLMediaElement::setRemotePlaybackClient(WebRemotePlaybackClient* client)
|
| @@ -3699,7 +3689,9 @@ void HTMLMediaElement::setRemotePlaybackClient(WebRemotePlaybackClient* client)
|
| // TODO(liberato): remove once autoplay gesture override experiment concludes.
|
| void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
|
| {
|
| - m_autoplayHelper.triggerAutoplayViewportCheckForTesting();
|
| + if (FrameView* view = document().view())
|
| + m_autoplayHelper->positionChanged(view->rootFrameToContents(view->computeVisibleArea()));
|
| + m_autoplayHelper->triggerAutoplayViewportCheckForTesting();
|
| }
|
|
|
| void HTMLMediaElement::scheduleResolvePlayPromises()
|
| @@ -3831,4 +3823,43 @@ DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl)
|
| visitor->trace(m_client);
|
| }
|
|
|
| +HTMLMediaElement::AutoplayHelperClientImpl::~AutoplayHelperClientImpl()
|
| +{
|
| +}
|
| +
|
| +bool HTMLMediaElement::AutoplayHelperClientImpl::isLegacyViewportType()
|
| +{
|
| + return m_element->document().viewportDescription().isLegacyViewportType();
|
| +}
|
| +
|
| +PageVisibilityState HTMLMediaElement::AutoplayHelperClientImpl::pageVisibilityState() const
|
| +{
|
| + return m_element->document().pageVisibilityState();
|
| +}
|
| +
|
| +String HTMLMediaElement::AutoplayHelperClientImpl::autoplayExperimentMode() const
|
| +{
|
| + String mode;
|
| + if (m_element->document().settings())
|
| + mode = m_element->document().settings()->autoplayExperimentMode();
|
| +
|
| + return mode;
|
| +}
|
| +
|
| +void HTMLMediaElement::AutoplayHelperClientImpl::setRequestPositionUpdates(bool request)
|
| +{
|
| + if (LayoutObject* layoutObject = m_element->layoutObject()) {
|
| + LayoutMediaItem layoutMediaItem = LayoutMediaItem(toLayoutMedia(layoutObject));
|
| + layoutMediaItem.setRequestPositionUpdates(request);
|
| + }
|
| +}
|
| +
|
| +IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() const
|
| +{
|
| + IntRect result;
|
| + if (LayoutObject* object = m_element->layoutObject())
|
| + result = object->absoluteBoundingBoxRect();
|
| + return result;
|
| +}
|
| +
|
| } // namespace blink
|
|
|