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

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: update expectations Created 4 years, 8 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 773 matching lines...) Expand 10 before | Expand all | Expand 10 after
784 m_displayMode = Unknown; 784 m_displayMode = Unknown;
785 785
786 // 1 - Abort any already-running instance of the resource selection algorith m for this element. 786 // 1 - Abort any already-running instance of the resource selection algorith m for this element.
787 m_loadState = WaitingForSource; 787 m_loadState = WaitingForSource;
788 m_currentSourceNode = nullptr; 788 m_currentSourceNode = nullptr;
789 789
790 // 2 - If there are any tasks from the media element's media element event t ask source in 790 // 2 - If there are any tasks from the media element's media element event t ask source in
791 // one of the task queues, then remove those tasks. 791 // one of the task queues, then remove those tasks.
792 cancelPendingEventsAndCallbacks(); 792 cancelPendingEventsAndCallbacks();
793 793
794 for (auto& resolver : m_playPromiseResolvers)
795 m_playPromiseRejectList.append(resolver);
philipj_slow 2016/04/06 14:12:59 m_playPromiseRejectList.append(m_playPromiseResolv
mlamouri (slow - plz ping) 2016/04/12 14:36:20 Done. Here and other places.
796 m_playPromiseResolvers.clear();
794 rejectPlayPromises(AbortError, "The play() request was interrupted by a new load request."); 797 rejectPlayPromises(AbortError, "The play() request was interrupted by a new load request.");
795 798
796 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETW ORK_IDLE, queue 799 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETW ORK_IDLE, queue
797 // a task to fire a simple event named abort at the media element. 800 // a task to fire a simple event named abort at the media element.
798 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) 801 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
799 scheduleEvent(EventTypeNames::abort); 802 scheduleEvent(EventTypeNames::abort);
800 803
801 resetMediaPlayerAndMediaSource(); 804 resetMediaPlayerAndMediaSource();
802 805
803 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps 806 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
(...skipping 1200 matching lines...) Expand 10 before | Expand all | Expand 10 after
2004 2007
2005 WebMediaPlayer::Preload preload = preloadType(); 2008 WebMediaPlayer::Preload preload = preloadType();
2006 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone) 2009 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone)
2007 return WebMediaPlayer::PreloadMetaData; 2010 return WebMediaPlayer::PreloadMetaData;
2008 2011
2009 return preload; 2012 return preload;
2010 } 2013 }
2011 2014
2012 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) 2015 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState)
2013 { 2016 {
2017 // We have to share the same logic for internal and external callers. The
2018 // internal callers do not want to receive a Promise back but when ::play()
2019 // is called, |m_playPromiseResolvers| needs to be populated. What this code do
philipj_slow 2016/04/06 14:12:59 s/do then/does/
mlamouri (slow - plz ping) 2016/04/12 14:36:20 Done.
2020 // then is to populate |m_playPromiseResolvers| before calling ::play() and remove
2021 // the Promise if ::play() failed.
2022 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2023 ScriptPromise promise = resolver->promise();
2024 m_playPromiseResolvers.append(resolver);
philipj_slow 2016/04/06 14:12:59 I don't follow why this change was needed. Does it
mlamouri (slow - plz ping) 2016/04/12 14:36:20 Assume the media is already playing. It will resol
2025
2014 Nullable<ExceptionCode> code = play(); 2026 Nullable<ExceptionCode> code = play();
2015 if (!code.isNull()) { 2027 if (!code.isNull()) {
2028 ASSERT(!m_playPromiseResolvers.isEmpty());
2029 m_playPromiseResolvers.removeLast();
2030
2016 String message; 2031 String message;
2017 switch (code.get()) { 2032 switch (code.get()) {
2018 case NotAllowedError: 2033 case NotAllowedError:
2019 message = "play() can only be initiated by a user gesture."; 2034 message = "play() can only be initiated by a user gesture.";
2020 break; 2035 break;
2021 case NotSupportedError: 2036 case NotSupportedError:
2022 message = "The element has no supported sources."; 2037 message = "The element has no supported sources.";
2023 break; 2038 break;
2024 default: 2039 default:
2025 ASSERT_NOT_REACHED(); 2040 ASSERT_NOT_REACHED();
2026 } 2041 }
2027 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message)); 2042 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message));
2028 } 2043 }
2029 2044
2030 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2031 ScriptPromise promise = resolver->promise();
2032
2033 m_playResolvers.append(resolver);
2034 return promise; 2045 return promise;
2035 } 2046 }
2036 2047
2037 Nullable<ExceptionCode> HTMLMediaElement::play() 2048 Nullable<ExceptionCode> HTMLMediaElement::play()
2038 { 2049 {
2039 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this); 2050 WTF_LOG(Media, "HTMLMediaElement::play(%p)", this);
2040 2051
2041 m_autoplayHelper->playMethodCalled(); 2052 m_autoplayHelper->playMethodCalled();
2042 2053
2043 if (!UserGestureIndicator::processingUserGesture()) { 2054 if (!UserGestureIndicator::processingUserGesture()) {
(...skipping 1582 matching lines...) Expand 10 before | Expand all | Expand 10 after
3626 visitor->trace(m_asyncEventQueue); 3637 visitor->trace(m_asyncEventQueue);
3627 visitor->trace(m_error); 3638 visitor->trace(m_error);
3628 visitor->trace(m_currentSourceNode); 3639 visitor->trace(m_currentSourceNode);
3629 visitor->trace(m_nextChildNodeToConsider); 3640 visitor->trace(m_nextChildNodeToConsider);
3630 visitor->trace(m_mediaSource); 3641 visitor->trace(m_mediaSource);
3631 visitor->trace(m_audioTracks); 3642 visitor->trace(m_audioTracks);
3632 visitor->trace(m_videoTracks); 3643 visitor->trace(m_videoTracks);
3633 visitor->trace(m_cueTimeline); 3644 visitor->trace(m_cueTimeline);
3634 visitor->trace(m_textTracks); 3645 visitor->trace(m_textTracks);
3635 visitor->trace(m_textTracksWhenResourceSelectionBegan); 3646 visitor->trace(m_textTracksWhenResourceSelectionBegan);
3636 visitor->trace(m_playResolvers); 3647 visitor->trace(m_playPromiseResolvers);
3648 visitor->trace(m_playPromiseResolveList);
3649 visitor->trace(m_playPromiseRejectList);
3637 visitor->trace(m_audioSourceProvider); 3650 visitor->trace(m_audioSourceProvider);
3638 visitor->trace(m_autoplayHelperClient); 3651 visitor->trace(m_autoplayHelperClient);
3639 visitor->trace(m_autoplayHelper); 3652 visitor->trace(m_autoplayHelper);
3640 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this); 3653 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this);
3641 Supplementable<HTMLMediaElement>::trace(visitor); 3654 Supplementable<HTMLMediaElement>::trace(visitor);
3642 HTMLElement::trace(visitor); 3655 HTMLElement::trace(visitor);
3643 ActiveDOMObject::trace(visitor); 3656 ActiveDOMObject::trace(visitor);
3644 } 3657 }
3645 3658
3646 void HTMLMediaElement::createPlaceholderTracksIfNecessary() 3659 void HTMLMediaElement::createPlaceholderTracksIfNecessary()
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
3708 // TODO(liberato): remove once autoplay gesture override experiment concludes. 3721 // TODO(liberato): remove once autoplay gesture override experiment concludes.
3709 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() 3722 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
3710 { 3723 {
3711 if (FrameView* view = document().view()) 3724 if (FrameView* view = document().view())
3712 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea())); 3725 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea()));
3713 m_autoplayHelper->triggerAutoplayViewportCheckForTesting(); 3726 m_autoplayHelper->triggerAutoplayViewportCheckForTesting();
3714 } 3727 }
3715 3728
3716 void HTMLMediaElement::scheduleResolvePlayPromises() 3729 void HTMLMediaElement::scheduleResolvePlayPromises()
3717 { 3730 {
3718 // Per spec, if there are two tasks in the queue, the first task will remove 3731 // TODO(mlamouri): per spec, we should create a new task but we can't create
3719 // all the pending promises making the second task useless unless a promise 3732 // a new cancellable task without cancelling the previous one. There are two
3720 // can be added between the first and second task being run which is not 3733 // approaches then: cancel the previous task and create a new one with the
3721 // possible at the moment. 3734 // appended promise list or append the new promise to the current list. The
philipj_slow 2016/04/06 14:12:59 Hmm, so these are two decidedly not great options.
mlamouri (slow - plz ping) 2016/04/12 14:36:20 I believe I'm doing what you are suggesting here o
3735 // former approach is prefered because it might be the less observable
3736 // change.
3737 ASSERT(m_playPromiseResolveList.isEmpty() || m_playPromiseResolveTask->isPen ding());
3738 if (m_playPromiseResolvers.isEmpty())
3739 return;
3740
3741 for (auto& resolver : m_playPromiseResolvers)
3742 m_playPromiseResolveList.append(resolver);
3743 m_playPromiseResolvers.clear();
3744
3722 if (m_playPromiseResolveTask->isPending()) 3745 if (m_playPromiseResolveTask->isPending())
3723 return; 3746 return;
3724 3747
3725 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate()); 3748 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate());
3726 } 3749 }
3727 3750
3728 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code) 3751 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code)
3729 { 3752 {
3730 // Per spec, if there are two tasks in the queue, the first task will remove 3753 // TODO(mlamouri): per spec, we should create a new task but we can't create
3731 // all the pending promises making the second task useless unless a promise 3754 // a new cancellable task without cancelling the previous one. There are two
3732 // can be added between the first and second task being run which is not 3755 // approaches then: cancel the previous task and create a new one with the
3733 // possible at the moment. 3756 // appended promise list or append the new promise to the current list. The
3757 // former approach is prefered because it might be the less observable
3758 // change.
3759 ASSERT(m_playPromiseRejectList.isEmpty() || m_playPromiseRejectTask->isPendi ng());
3760 if (m_playPromiseResolvers.isEmpty())
3761 return;
3762
3763 for (auto& resolver : m_playPromiseResolvers)
3764 m_playPromiseRejectList.append(resolver);
3765 m_playPromiseResolvers.clear();
3766
3734 if (m_playPromiseRejectTask->isPending()) 3767 if (m_playPromiseRejectTask->isPending())
3735 return; 3768 return;
3736 3769
3737 // TODO(mlamouri): because cancellable tasks can't take parameters, the 3770 // TODO(mlamouri): because cancellable tasks can't take parameters, the
3738 // error code needs to be saved. 3771 // error code needs to be saved.
3739 m_playPromiseErrorCode = code; 3772 m_playPromiseErrorCode = code;
3740 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate()); 3773 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate());
3741 } 3774 }
3742 3775
3743 void HTMLMediaElement::scheduleNotifyPlaying() 3776 void HTMLMediaElement::scheduleNotifyPlaying()
3744 { 3777 {
3745 scheduleEvent(EventTypeNames::playing); 3778 scheduleEvent(EventTypeNames::playing);
3746 scheduleResolvePlayPromises(); 3779 scheduleResolvePlayPromises();
3747 } 3780 }
3748 3781
3749 void HTMLMediaElement::resolvePlayPromises() 3782 void HTMLMediaElement::resolvePlayPromises()
3750 { 3783 {
3751 for (auto& resolver: m_playResolvers) 3784 for (auto& resolver: m_playPromiseResolveList)
3752 resolver->resolve(); 3785 resolver->resolve();
3753 3786
3754 m_playResolvers.clear(); 3787 m_playPromiseResolveList.clear();
3755 } 3788 }
3756 3789
3757 void HTMLMediaElement::rejectPlayPromises() 3790 void HTMLMediaElement::rejectPlayPromises()
3758 { 3791 {
3759 // TODO(mlamouri): the message is generated based on the code because 3792 // TODO(mlamouri): the message is generated based on the code because
3760 // arguments can't be passed to a cancellable task. In order to save space 3793 // arguments can't be passed to a cancellable task. In order to save space
3761 // used by the object, the string isn't saved. 3794 // used by the object, the string isn't saved.
3762 ASSERT(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError); 3795 ASSERT(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError);
3763 if (m_playPromiseErrorCode == AbortError) 3796 if (m_playPromiseErrorCode == AbortError)
3764 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause()."); 3797 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause().");
3765 else 3798 else
3766 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found."); 3799 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found.");
3767 } 3800 }
3768 3801
3769 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age) 3802 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age)
3770 { 3803 {
3771 ASSERT(code == AbortError || code == NotSupportedError); 3804 ASSERT(code == AbortError || code == NotSupportedError);
3772 3805
3773 for (auto& resolver: m_playResolvers) 3806 for (auto& resolver: m_playPromiseRejectList)
3774 resolver->reject(DOMException::create(code, message)); 3807 resolver->reject(DOMException::create(code, message));
3775 3808
3776 m_playResolvers.clear(); 3809 m_playPromiseRejectList.clear();
3777 } 3810 }
3778 3811
3779 void HTMLMediaElement::clearWeakMembers(Visitor* visitor) 3812 void HTMLMediaElement::clearWeakMembers(Visitor* visitor)
3780 { 3813 {
3781 if (!Heap::isHeapObjectAlive(m_audioSourceNode)) 3814 if (!Heap::isHeapObjectAlive(m_audioSourceNode))
3782 getAudioSourceProvider().setClient(nullptr); 3815 getAudioSourceProvider().setClient(nullptr);
3783 } 3816 }
3784 3817
3785 void HTMLMediaElement::AudioSourceProviderImpl::wrap(WebAudioSourceProvider* pro vider) 3818 void HTMLMediaElement::AudioSourceProviderImpl::wrap(WebAudioSourceProvider* pro vider)
3786 { 3819 {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
3882 } 3915 }
3883 3916
3884 #if !ENABLE(OILPAN) 3917 #if !ENABLE(OILPAN)
3885 WeakPtr<HTMLMediaElement> HTMLMediaElement::createWeakPtr() 3918 WeakPtr<HTMLMediaElement> HTMLMediaElement::createWeakPtr()
3886 { 3919 {
3887 return m_weakPtrFactory.createWeakPtr(); 3920 return m_weakPtrFactory.createWeakPtr();
3888 } 3921 }
3889 #endif 3922 #endif
3890 3923
3891 } // namespace blink 3924 } // 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