Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: third_party/WebKit/Source/core/html/HTMLMediaElement.cpp

Issue 1865933002: Fix race when HTMLMediaElement.play() is called just after pause(). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/html/HTMLMediaElement.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 416 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 , m_textTracksVisible(false) 427 , m_textTracksVisible(false)
428 , m_shouldPerformAutomaticTrackSelection(true) 428 , m_shouldPerformAutomaticTrackSelection(true)
429 , m_tracksAreReady(true) 429 , m_tracksAreReady(true)
430 , m_processingPreferenceChange(false) 430 , m_processingPreferenceChange(false)
431 , m_remoteRoutesAvailable(false) 431 , m_remoteRoutesAvailable(false)
432 , m_playingRemotely(false) 432 , m_playingRemotely(false)
433 , m_inOverlayFullscreenVideo(false) 433 , m_inOverlayFullscreenVideo(false)
434 , m_audioTracks(AudioTrackList::create(*this)) 434 , m_audioTracks(AudioTrackList::create(*this))
435 , m_videoTracks(VideoTrackList::create(*this)) 435 , m_videoTracks(VideoTrackList::create(*this))
436 , m_textTracks(nullptr) 436 , m_textTracks(nullptr)
437 , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaEl ement::resolvePlayPromises)) 437 , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaEl ement::resolveScheduledPlayPromises))
438 , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaEle ment::rejectPlayPromises)) 438 , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaEle ment::rejectScheduledPlayPromises))
439 , m_audioSourceNode(nullptr) 439 , m_audioSourceNode(nullptr)
440 , m_autoplayHelperClient(AutoplayHelperClientImpl::create(this)) 440 , m_autoplayHelperClient(AutoplayHelperClientImpl::create(this))
441 , m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.g et())) 441 , m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.g et()))
442 , m_remotePlaybackClient(nullptr) 442 , m_remotePlaybackClient(nullptr)
443 { 443 {
444 ThreadState::current()->registerPreFinalizer(this); 444 ThreadState::current()->registerPreFinalizer(this);
445 445
446 DVLOG(MEDIA_LOG_LEVEL) << "HTMLMediaElement(" << (void*)this << ")"; 446 DVLOG(MEDIA_LOG_LEVEL) << "HTMLMediaElement(" << (void*)this << ")";
447 447
448 // If any experiment is enabled, then we want to enable a user gesture by 448 // If any experiment is enabled, then we want to enable a user gesture by
(...skipping 938 matching lines...) Expand 10 before | Expand all | Expand 10 after
1387 1387
1388 void HTMLMediaElement::cancelPendingEventsAndCallbacks() 1388 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1389 { 1389 {
1390 DVLOG(MEDIA_LOG_LEVEL) << "cancelPendingEventsAndCallbacks(" << (void*)this << ")"; 1390 DVLOG(MEDIA_LOG_LEVEL) << "cancelPendingEventsAndCallbacks(" << (void*)this << ")";
1391 m_asyncEventQueue->cancelAllEvents(); 1391 m_asyncEventQueue->cancelAllEvents();
1392 1392
1393 for (HTMLSourceElement* source = Traversal<HTMLSourceElement>::firstChild(*t his); source; source = Traversal<HTMLSourceElement>::nextSibling(*source)) 1393 for (HTMLSourceElement* source = Traversal<HTMLSourceElement>::firstChild(*t his); source; source = Traversal<HTMLSourceElement>::nextSibling(*source))
1394 source->cancelPendingErrorEvent(); 1394 source->cancelPendingErrorEvent();
1395 1395
1396 m_playPromiseResolveTask->cancel(); 1396 m_playPromiseResolveTask->cancel();
1397 m_playPromiseResolveList.clear();
1397 m_playPromiseRejectTask->cancel(); 1398 m_playPromiseRejectTask->cancel();
1399 m_playPromiseRejectList.clear();
1398 } 1400 }
1399 1401
1400 void HTMLMediaElement::networkStateChanged() 1402 void HTMLMediaElement::networkStateChanged()
1401 { 1403 {
1402 setNetworkState(webMediaPlayer()->getNetworkState()); 1404 setNetworkState(webMediaPlayer()->getNetworkState());
1403 } 1405 }
1404 1406
1405 void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error) 1407 void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error)
1406 { 1408 {
1407 stopPeriodicTimers(); 1409 stopPeriodicTimers();
(...skipping 606 matching lines...) Expand 10 before | Expand all | Expand 10 after
2014 2016
2015 WebMediaPlayer::Preload preload = preloadType(); 2017 WebMediaPlayer::Preload preload = preloadType();
2016 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone) 2018 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone)
2017 return WebMediaPlayer::PreloadMetaData; 2019 return WebMediaPlayer::PreloadMetaData;
2018 2020
2019 return preload; 2021 return preload;
2020 } 2022 }
2021 2023
2022 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) 2024 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState)
2023 { 2025 {
2026 // We have to share the same logic for internal and external callers. The
2027 // internal callers do not want to receive a Promise back but when ::play()
2028 // is called, |m_playPromiseResolvers| needs to be populated. What this code
2029 // does is to populate |m_playPromiseResolvers| before calling ::play() and
2030 // remove the Promise if ::play() failed.
2031 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2032 ScriptPromise promise = resolver->promise();
2033 m_playPromiseResolvers.append(resolver);
2034
2024 Nullable<ExceptionCode> code = play(); 2035 Nullable<ExceptionCode> code = play();
2025 if (!code.isNull()) { 2036 if (!code.isNull()) {
2037 DCHECK(!m_playPromiseResolvers.isEmpty());
2038 m_playPromiseResolvers.removeLast();
2039
2026 String message; 2040 String message;
2027 switch (code.get()) { 2041 switch (code.get()) {
2028 case NotAllowedError: 2042 case NotAllowedError:
2029 message = "play() can only be initiated by a user gesture."; 2043 message = "play() can only be initiated by a user gesture.";
2030 break; 2044 break;
2031 case NotSupportedError: 2045 case NotSupportedError:
2032 message = "The element has no supported sources."; 2046 message = "The element has no supported sources.";
2033 break; 2047 break;
2034 default: 2048 default:
2035 NOTREACHED(); 2049 NOTREACHED();
2036 } 2050 }
2037 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message)); 2051 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message));
2038 } 2052 }
2039 2053
2040 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2041 ScriptPromise promise = resolver->promise();
2042
2043 m_playResolvers.append(resolver);
2044 return promise; 2054 return promise;
2045 } 2055 }
2046 2056
2047 Nullable<ExceptionCode> HTMLMediaElement::play() 2057 Nullable<ExceptionCode> HTMLMediaElement::play()
2048 { 2058 {
2049 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")"; 2059 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")";
2050 2060
2051 m_autoplayHelper->playMethodCalled(); 2061 m_autoplayHelper->playMethodCalled();
2052 2062
2053 if (!UserGestureIndicator::processingUserGesture()) { 2063 if (!UserGestureIndicator::processingUserGesture()) {
(...skipping 1541 matching lines...) Expand 10 before | Expand all | Expand 10 after
3595 visitor->trace(m_asyncEventQueue); 3605 visitor->trace(m_asyncEventQueue);
3596 visitor->trace(m_error); 3606 visitor->trace(m_error);
3597 visitor->trace(m_currentSourceNode); 3607 visitor->trace(m_currentSourceNode);
3598 visitor->trace(m_nextChildNodeToConsider); 3608 visitor->trace(m_nextChildNodeToConsider);
3599 visitor->trace(m_mediaSource); 3609 visitor->trace(m_mediaSource);
3600 visitor->trace(m_audioTracks); 3610 visitor->trace(m_audioTracks);
3601 visitor->trace(m_videoTracks); 3611 visitor->trace(m_videoTracks);
3602 visitor->trace(m_cueTimeline); 3612 visitor->trace(m_cueTimeline);
3603 visitor->trace(m_textTracks); 3613 visitor->trace(m_textTracks);
3604 visitor->trace(m_textTracksWhenResourceSelectionBegan); 3614 visitor->trace(m_textTracksWhenResourceSelectionBegan);
3605 visitor->trace(m_playResolvers); 3615 visitor->trace(m_playPromiseResolvers);
3616 visitor->trace(m_playPromiseResolveList);
3617 visitor->trace(m_playPromiseRejectList);
3606 visitor->trace(m_audioSourceProvider); 3618 visitor->trace(m_audioSourceProvider);
3607 visitor->trace(m_autoplayHelperClient); 3619 visitor->trace(m_autoplayHelperClient);
3608 visitor->trace(m_autoplayHelper); 3620 visitor->trace(m_autoplayHelper);
3609 visitor->trace(m_srcObject); 3621 visitor->trace(m_srcObject);
3610 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this); 3622 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this);
3611 Supplementable<HTMLMediaElement>::trace(visitor); 3623 Supplementable<HTMLMediaElement>::trace(visitor);
3612 HTMLElement::trace(visitor); 3624 HTMLElement::trace(visitor);
3613 ActiveDOMObject::trace(visitor); 3625 ActiveDOMObject::trace(visitor);
3614 } 3626 }
3615 3627
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
3692 // TODO(liberato): remove once autoplay gesture override experiment concludes. 3704 // TODO(liberato): remove once autoplay gesture override experiment concludes.
3693 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() 3705 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
3694 { 3706 {
3695 if (FrameView* view = document().view()) 3707 if (FrameView* view = document().view())
3696 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea())); 3708 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea()));
3697 m_autoplayHelper->triggerAutoplayViewportCheckForTesting(); 3709 m_autoplayHelper->triggerAutoplayViewportCheckForTesting();
3698 } 3710 }
3699 3711
3700 void HTMLMediaElement::scheduleResolvePlayPromises() 3712 void HTMLMediaElement::scheduleResolvePlayPromises()
3701 { 3713 {
3702 // Per spec, if there are two tasks in the queue, the first task will remove 3714 // TODO(mlamouri): per spec, we should create a new task but we can't create
3703 // all the pending promises making the second task useless unless a promise 3715 // a new cancellable task without cancelling the previous one. There are two
3704 // can be added between the first and second task being run which is not 3716 // approaches then: cancel the previous task and create a new one with the
3705 // possible at the moment. 3717 // appended promise list or append the new promise to the current list. The
3718 // latter approach is preferred because it might be the less observable
3719 // change.
3720 DCHECK(m_playPromiseResolveList.isEmpty() || m_playPromiseResolveTask->isPen ding());
3721 if (m_playPromiseResolvers.isEmpty())
3722 return;
3723
3724 m_playPromiseResolveList.appendVector(m_playPromiseResolvers);
3725 m_playPromiseResolvers.clear();
3726
3706 if (m_playPromiseResolveTask->isPending()) 3727 if (m_playPromiseResolveTask->isPending())
3707 return; 3728 return;
3708 3729
3709 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate()); 3730 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate());
3710 } 3731 }
3711 3732
3712 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code) 3733 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code)
3713 { 3734 {
3714 // Per spec, if there are two tasks in the queue, the first task will remove 3735 // TODO(mlamouri): per spec, we should create a new task but we can't create
3715 // all the pending promises making the second task useless unless a promise 3736 // a new cancellable task without cancelling the previous one. There are two
3716 // can be added between the first and second task being run which is not 3737 // approaches then: cancel the previous task and create a new one with the
3717 // possible at the moment. 3738 // appended promise list or append the new promise to the current list. The
3739 // latter approach is preferred because it might be the less observable
3740 // change.
3741 DCHECK(m_playPromiseRejectList.isEmpty() || m_playPromiseRejectTask->isPendi ng());
3742 if (m_playPromiseResolvers.isEmpty())
3743 return;
3744
3745 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3746 m_playPromiseResolvers.clear();
3747
3718 if (m_playPromiseRejectTask->isPending()) 3748 if (m_playPromiseRejectTask->isPending())
3719 return; 3749 return;
3720 3750
3721 // TODO(mlamouri): because cancellable tasks can't take parameters, the 3751 // TODO(mlamouri): because cancellable tasks can't take parameters, the
3722 // error code needs to be saved. 3752 // error code needs to be saved.
3723 m_playPromiseErrorCode = code; 3753 m_playPromiseErrorCode = code;
3724 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate()); 3754 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate());
3725 } 3755 }
3726 3756
3727 void HTMLMediaElement::scheduleNotifyPlaying() 3757 void HTMLMediaElement::scheduleNotifyPlaying()
3728 { 3758 {
3729 scheduleEvent(EventTypeNames::playing); 3759 scheduleEvent(EventTypeNames::playing);
3730 scheduleResolvePlayPromises(); 3760 scheduleResolvePlayPromises();
3731 } 3761 }
3732 3762
3733 void HTMLMediaElement::resolvePlayPromises() 3763 void HTMLMediaElement::resolveScheduledPlayPromises()
3734 { 3764 {
3735 for (auto& resolver: m_playResolvers) 3765 for (auto& resolver: m_playPromiseResolveList)
3736 resolver->resolve(); 3766 resolver->resolve();
3737 3767
3738 m_playResolvers.clear(); 3768 m_playPromiseResolveList.clear();
3739 } 3769 }
3740 3770
3741 void HTMLMediaElement::rejectPlayPromises() 3771 void HTMLMediaElement::rejectScheduledPlayPromises()
3742 { 3772 {
3743 // TODO(mlamouri): the message is generated based on the code because 3773 // TODO(mlamouri): the message is generated based on the code because
3744 // arguments can't be passed to a cancellable task. In order to save space 3774 // arguments can't be passed to a cancellable task. In order to save space
3745 // used by the object, the string isn't saved. 3775 // used by the object, the string isn't saved.
3746 DCHECK(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError); 3776 DCHECK(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError);
3747 if (m_playPromiseErrorCode == AbortError) 3777 if (m_playPromiseErrorCode == AbortError)
3748 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause()."); 3778 rejectPlayPromisesInternal(AbortError, "The play() request was interrupt ed by a call to pause().");
3749 else 3779 else
3750 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found."); 3780 rejectPlayPromisesInternal(NotSupportedError, "Failed to load because no supported source was found.");
3751 } 3781 }
3752 3782
3753 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age) 3783 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age)
3754 { 3784 {
3785 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3786 m_playPromiseResolvers.clear();
3787 rejectPlayPromisesInternal(code, message);
3788 }
3789
3790 void HTMLMediaElement::rejectPlayPromisesInternal(ExceptionCode code, const Stri ng& message)
3791 {
3755 DCHECK(code == AbortError || code == NotSupportedError); 3792 DCHECK(code == AbortError || code == NotSupportedError);
3756 3793
3757 for (auto& resolver: m_playResolvers) 3794 for (auto& resolver: m_playPromiseRejectList)
3758 resolver->reject(DOMException::create(code, message)); 3795 resolver->reject(DOMException::create(code, message));
3759 3796
3760 m_playResolvers.clear(); 3797 m_playPromiseRejectList.clear();
3761 } 3798 }
3762 3799
3763 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const 3800 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const
3764 { 3801 {
3765 if (isHTMLVideoElement()) { 3802 if (isHTMLVideoElement()) {
3766 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax)); 3803 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax));
3767 return histogram; 3804 return histogram;
3768 } 3805 }
3769 3806
3770 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax)); 3807 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax));
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
3881 3918
3882 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst 3919 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst
3883 { 3920 {
3884 IntRect result; 3921 IntRect result;
3885 if (LayoutObject* object = m_element->layoutObject()) 3922 if (LayoutObject* object = m_element->layoutObject())
3886 result = object->absoluteBoundingBoxRect(); 3923 result = object->absoluteBoundingBoxRect();
3887 return result; 3924 return result;
3888 } 3925 }
3889 3926
3890 } // namespace blink 3927 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/html/HTMLMediaElement.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698