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

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: rebase and match spec 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
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 1565 matching lines...) Expand 10 before | Expand all | Expand 10 after
2014 2014
2015 WebMediaPlayer::Preload preload = preloadType(); 2015 WebMediaPlayer::Preload preload = preloadType();
2016 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone) 2016 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone)
2017 return WebMediaPlayer::PreloadMetaData; 2017 return WebMediaPlayer::PreloadMetaData;
2018 2018
2019 return preload; 2019 return preload;
2020 } 2020 }
2021 2021
2022 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) 2022 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState)
2023 { 2023 {
2024 // We have to share the same logic for internal and external callers. The
2025 // internal callers do not want to receive a Promise back but when ::play()
2026 // is called, |m_playPromiseResolvers| needs to be populated. What this code
2027 // does is to populate |m_playPromiseResolvers| before calling ::play() and
2028 // remove the Promise if ::play() failed.
2029 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2030 ScriptPromise promise = resolver->promise();
2031 m_playPromiseResolvers.append(resolver);
2032
2024 Nullable<ExceptionCode> code = play(); 2033 Nullable<ExceptionCode> code = play();
2025 if (!code.isNull()) { 2034 if (!code.isNull()) {
2035 DCHECK(!m_playPromiseResolvers.isEmpty());
2036 m_playPromiseResolvers.removeLast();
2037
2026 String message; 2038 String message;
2027 switch (code.get()) { 2039 switch (code.get()) {
2028 case NotAllowedError: 2040 case NotAllowedError:
2029 message = "play() can only be initiated by a user gesture."; 2041 message = "play() can only be initiated by a user gesture.";
2030 break; 2042 break;
2031 case NotSupportedError: 2043 case NotSupportedError:
2032 message = "The element has no supported sources."; 2044 message = "The element has no supported sources.";
2033 break; 2045 break;
2034 default: 2046 default:
2035 NOTREACHED(); 2047 NOTREACHED();
2036 } 2048 }
2037 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message)); 2049 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message));
2038 } 2050 }
2039 2051
2040 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2041 ScriptPromise promise = resolver->promise();
2042
2043 m_playResolvers.append(resolver);
2044 return promise; 2052 return promise;
2045 } 2053 }
2046 2054
2047 Nullable<ExceptionCode> HTMLMediaElement::play() 2055 Nullable<ExceptionCode> HTMLMediaElement::play()
2048 { 2056 {
2049 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")"; 2057 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")";
2050 2058
2051 m_autoplayHelper->playMethodCalled(); 2059 m_autoplayHelper->playMethodCalled();
2052 2060
2053 if (!UserGestureIndicator::processingUserGesture()) { 2061 if (!UserGestureIndicator::processingUserGesture()) {
(...skipping 1541 matching lines...) Expand 10 before | Expand all | Expand 10 after
3595 visitor->trace(m_asyncEventQueue); 3603 visitor->trace(m_asyncEventQueue);
3596 visitor->trace(m_error); 3604 visitor->trace(m_error);
3597 visitor->trace(m_currentSourceNode); 3605 visitor->trace(m_currentSourceNode);
3598 visitor->trace(m_nextChildNodeToConsider); 3606 visitor->trace(m_nextChildNodeToConsider);
3599 visitor->trace(m_mediaSource); 3607 visitor->trace(m_mediaSource);
3600 visitor->trace(m_audioTracks); 3608 visitor->trace(m_audioTracks);
3601 visitor->trace(m_videoTracks); 3609 visitor->trace(m_videoTracks);
3602 visitor->trace(m_cueTimeline); 3610 visitor->trace(m_cueTimeline);
3603 visitor->trace(m_textTracks); 3611 visitor->trace(m_textTracks);
3604 visitor->trace(m_textTracksWhenResourceSelectionBegan); 3612 visitor->trace(m_textTracksWhenResourceSelectionBegan);
3605 visitor->trace(m_playResolvers); 3613 visitor->trace(m_playPromiseResolvers);
3614 visitor->trace(m_playPromiseResolveList);
3615 visitor->trace(m_playPromiseRejectList);
3606 visitor->trace(m_audioSourceProvider); 3616 visitor->trace(m_audioSourceProvider);
3607 visitor->trace(m_autoplayHelperClient); 3617 visitor->trace(m_autoplayHelperClient);
3608 visitor->trace(m_autoplayHelper); 3618 visitor->trace(m_autoplayHelper);
3609 visitor->trace(m_srcObject); 3619 visitor->trace(m_srcObject);
3610 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this); 3620 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this);
3611 Supplementable<HTMLMediaElement>::trace(visitor); 3621 Supplementable<HTMLMediaElement>::trace(visitor);
3612 HTMLElement::trace(visitor); 3622 HTMLElement::trace(visitor);
3613 ActiveDOMObject::trace(visitor); 3623 ActiveDOMObject::trace(visitor);
3614 } 3624 }
3615 3625
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
3692 // TODO(liberato): remove once autoplay gesture override experiment concludes. 3702 // TODO(liberato): remove once autoplay gesture override experiment concludes.
3693 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() 3703 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
3694 { 3704 {
3695 if (FrameView* view = document().view()) 3705 if (FrameView* view = document().view())
3696 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea())); 3706 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea()));
3697 m_autoplayHelper->triggerAutoplayViewportCheckForTesting(); 3707 m_autoplayHelper->triggerAutoplayViewportCheckForTesting();
3698 } 3708 }
3699 3709
3700 void HTMLMediaElement::scheduleResolvePlayPromises() 3710 void HTMLMediaElement::scheduleResolvePlayPromises()
3701 { 3711 {
3702 // Per spec, if there are two tasks in the queue, the first task will remove 3712 // 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 3713 // 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 3714 // approaches then: cancel the previous task and create a new one with the
3705 // possible at the moment. 3715 // appended promise list or append the new promise to the current list. The
3716 // former approach is prefered because it might be the less observable
fs 2016/06/06 09:13:24 Nit: preferred (also below)
mlamouri (slow - plz ping) 2016/06/06 16:00:55 Done.
3717 // change.
3718 DCHECK(m_playPromiseResolveList.isEmpty() || m_playPromiseResolveTask->isPen ding());
3719 if (m_playPromiseResolvers.isEmpty())
3720 return;
3721
3722 m_playPromiseResolveList.appendVector(m_playPromiseResolvers);
3723 m_playPromiseResolvers.clear();
3724
3706 if (m_playPromiseResolveTask->isPending()) 3725 if (m_playPromiseResolveTask->isPending())
3707 return; 3726 return;
3708 3727
3709 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate()); 3728 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate());
3710 } 3729 }
3711 3730
3712 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code) 3731 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code)
3713 { 3732 {
3714 // Per spec, if there are two tasks in the queue, the first task will remove 3733 // 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 3734 // 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 3735 // approaches then: cancel the previous task and create a new one with the
3717 // possible at the moment. 3736 // appended promise list or append the new promise to the current list. The
3737 // former approach is prefered because it might be the less observable
whywhat 2016/06/06 13:08:57 nit: the code seems to be doing the latter, not fo
mlamouri (slow - plz ping) 2016/06/06 16:00:55 Done.
3738 // change.
3739 DCHECK(m_playPromiseRejectList.isEmpty() || m_playPromiseRejectTask->isPendi ng());
3740 if (m_playPromiseResolvers.isEmpty())
3741 return;
3742
3743 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3744 m_playPromiseResolvers.clear();
3745
3718 if (m_playPromiseRejectTask->isPending()) 3746 if (m_playPromiseRejectTask->isPending())
3719 return; 3747 return;
3720 3748
3721 // TODO(mlamouri): because cancellable tasks can't take parameters, the 3749 // TODO(mlamouri): because cancellable tasks can't take parameters, the
3722 // error code needs to be saved. 3750 // error code needs to be saved.
3723 m_playPromiseErrorCode = code; 3751 m_playPromiseErrorCode = code;
whywhat 2016/06/06 13:08:57 nit: should we update the current code for the pen
3724 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate()); 3752 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate());
3725 } 3753 }
3726 3754
3727 void HTMLMediaElement::scheduleNotifyPlaying() 3755 void HTMLMediaElement::scheduleNotifyPlaying()
3728 { 3756 {
3729 scheduleEvent(EventTypeNames::playing); 3757 scheduleEvent(EventTypeNames::playing);
3730 scheduleResolvePlayPromises(); 3758 scheduleResolvePlayPromises();
3731 } 3759 }
3732 3760
3733 void HTMLMediaElement::resolvePlayPromises() 3761 void HTMLMediaElement::resolveScheduledPlayPromises()
3734 { 3762 {
3735 for (auto& resolver: m_playResolvers) 3763 for (auto& resolver: m_playPromiseResolveList)
3736 resolver->resolve(); 3764 resolver->resolve();
3737 3765
3738 m_playResolvers.clear(); 3766 m_playPromiseResolveList.clear();
3739 } 3767 }
3740 3768
3741 void HTMLMediaElement::rejectPlayPromises() 3769 void HTMLMediaElement::rejectScheduledPlayPromises()
3742 { 3770 {
3743 // TODO(mlamouri): the message is generated based on the code because 3771 // 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 3772 // arguments can't be passed to a cancellable task. In order to save space
3745 // used by the object, the string isn't saved. 3773 // used by the object, the string isn't saved.
3746 DCHECK(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError); 3774 DCHECK(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError);
3747 if (m_playPromiseErrorCode == AbortError) 3775 if (m_playPromiseErrorCode == AbortError)
3748 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause()."); 3776 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause().");
3749 else 3777 else
3750 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found."); 3778 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found.");
3751 } 3779 }
3752 3780
3753 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age) 3781 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age)
3754 { 3782 {
3783 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3784 m_playPromiseResolvers.clear();
3785 rejectPlayPromisesInternal(code, message);
3786 }
3787
3788 void HTMLMediaElement::rejectPlayPromisesInternal(ExceptionCode code, const Stri ng& message)
whywhat 2016/06/06 13:08:57 nit: seems like this method could be inlined
mlamouri (slow - plz ping) 2016/06/06 16:00:55 No. I forgot to use it in `rejectScheduledPlayProm
3789 {
3755 DCHECK(code == AbortError || code == NotSupportedError); 3790 DCHECK(code == AbortError || code == NotSupportedError);
3756 3791
3757 for (auto& resolver: m_playResolvers) 3792 for (auto& resolver: m_playPromiseRejectList)
3758 resolver->reject(DOMException::create(code, message)); 3793 resolver->reject(DOMException::create(code, message));
3759 3794
3760 m_playResolvers.clear(); 3795 m_playPromiseRejectList.clear();
3761 } 3796 }
3762 3797
3763 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const 3798 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const
3764 { 3799 {
3765 if (isHTMLVideoElement()) { 3800 if (isHTMLVideoElement()) {
3766 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax)); 3801 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax));
3767 return histogram; 3802 return histogram;
3768 } 3803 }
3769 3804
3770 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax)); 3805 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax));
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
3881 3916
3882 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst 3917 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst
3883 { 3918 {
3884 IntRect result; 3919 IntRect result;
3885 if (LayoutObject* object = m_element->layoutObject()) 3920 if (LayoutObject* object = m_element->layoutObject())
3886 result = object->absoluteBoundingBoxRect(); 3921 result = object->absoluteBoundingBoxRect();
3887 return result; 3922 return result;
3888 } 3923 }
3889 3924
3890 } // namespace blink 3925 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698