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

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

Issue 2053333002: Fix race when HTMLMediaElement.play() is called just after pause(). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@2743
Patch Set: 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 426 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 , m_textTracksVisible(false) 437 , m_textTracksVisible(false)
438 , m_shouldPerformAutomaticTrackSelection(true) 438 , m_shouldPerformAutomaticTrackSelection(true)
439 , m_tracksAreReady(true) 439 , m_tracksAreReady(true)
440 , m_processingPreferenceChange(false) 440 , m_processingPreferenceChange(false)
441 , m_remoteRoutesAvailable(false) 441 , m_remoteRoutesAvailable(false)
442 , m_playingRemotely(false) 442 , m_playingRemotely(false)
443 , m_inOverlayFullscreenVideo(false) 443 , m_inOverlayFullscreenVideo(false)
444 , m_audioTracks(AudioTrackList::create(*this)) 444 , m_audioTracks(AudioTrackList::create(*this))
445 , m_videoTracks(VideoTrackList::create(*this)) 445 , m_videoTracks(VideoTrackList::create(*this))
446 , m_textTracks(nullptr) 446 , m_textTracks(nullptr)
447 , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaEl ement::resolvePlayPromises)) 447 , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaEl ement::resolveScheduledPlayPromises))
448 , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaEle ment::rejectPlayPromises)) 448 , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaEle ment::rejectScheduledPlayPromises))
449 , m_audioSourceNode(nullptr) 449 , m_audioSourceNode(nullptr)
450 , m_autoplayHelperClient(AutoplayHelperClientImpl::create(this)) 450 , m_autoplayHelperClient(AutoplayHelperClientImpl::create(this))
451 , m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.g et())) 451 , m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.g et()))
452 , m_remotePlaybackClient(nullptr) 452 , m_remotePlaybackClient(nullptr)
453 { 453 {
454 ThreadState::current()->registerPreFinalizer(this); 454 ThreadState::current()->registerPreFinalizer(this);
455 455
456 DVLOG(MEDIA_LOG_LEVEL) << "HTMLMediaElement(" << (void*)this << ")"; 456 DVLOG(MEDIA_LOG_LEVEL) << "HTMLMediaElement(" << (void*)this << ")";
457 457
458 // If any experiment is enabled, then we want to enable a user gesture by 458 // 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
1397 1397
1398 void HTMLMediaElement::cancelPendingEventsAndCallbacks() 1398 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1399 { 1399 {
1400 DVLOG(MEDIA_LOG_LEVEL) << "cancelPendingEventsAndCallbacks(" << (void*)this << ")"; 1400 DVLOG(MEDIA_LOG_LEVEL) << "cancelPendingEventsAndCallbacks(" << (void*)this << ")";
1401 m_asyncEventQueue->cancelAllEvents(); 1401 m_asyncEventQueue->cancelAllEvents();
1402 1402
1403 for (HTMLSourceElement* source = Traversal<HTMLSourceElement>::firstChild(*t his); source; source = Traversal<HTMLSourceElement>::nextSibling(*source)) 1403 for (HTMLSourceElement* source = Traversal<HTMLSourceElement>::firstChild(*t his); source; source = Traversal<HTMLSourceElement>::nextSibling(*source))
1404 source->cancelPendingErrorEvent(); 1404 source->cancelPendingErrorEvent();
1405 1405
1406 m_playPromiseResolveTask->cancel(); 1406 m_playPromiseResolveTask->cancel();
1407 m_playPromiseResolveList.clear();
1407 m_playPromiseRejectTask->cancel(); 1408 m_playPromiseRejectTask->cancel();
1409 m_playPromiseRejectList.clear();
1408 } 1410 }
1409 1411
1410 void HTMLMediaElement::networkStateChanged() 1412 void HTMLMediaElement::networkStateChanged()
1411 { 1413 {
1412 setNetworkState(webMediaPlayer()->getNetworkState()); 1414 setNetworkState(webMediaPlayer()->getNetworkState());
1413 } 1415 }
1414 1416
1415 void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error) 1417 void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error)
1416 { 1418 {
1417 stopPeriodicTimers(); 1419 stopPeriodicTimers();
(...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after
2022 2024
2023 WebMediaPlayer::Preload preload = preloadType(); 2025 WebMediaPlayer::Preload preload = preloadType();
2024 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone) 2026 if (m_ignorePreloadNone && preload == WebMediaPlayer::PreloadNone)
2025 return WebMediaPlayer::PreloadMetaData; 2027 return WebMediaPlayer::PreloadMetaData;
2026 2028
2027 return preload; 2029 return preload;
2028 } 2030 }
2029 2031
2030 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) 2032 ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState)
2031 { 2033 {
2034 // We have to share the same logic for internal and external callers. The
2035 // internal callers do not want to receive a Promise back but when ::play()
2036 // is called, |m_playPromiseResolvers| needs to be populated. What this code
2037 // does is to populate |m_playPromiseResolvers| before calling ::play() and
2038 // remove the Promise if ::play() failed.
2039 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2040 ScriptPromise promise = resolver->promise();
2041 m_playPromiseResolvers.append(resolver);
2042
2032 Nullable<ExceptionCode> code = play(); 2043 Nullable<ExceptionCode> code = play();
2033 if (!code.isNull()) { 2044 if (!code.isNull()) {
2045 DCHECK(!m_playPromiseResolvers.isEmpty());
2046 m_playPromiseResolvers.removeLast();
2047
2034 String message; 2048 String message;
2035 switch (code.get()) { 2049 switch (code.get()) {
2036 case NotAllowedError: 2050 case NotAllowedError:
2037 message = "play() can only be initiated by a user gesture."; 2051 message = "play() can only be initiated by a user gesture.";
2038 break; 2052 break;
2039 case NotSupportedError: 2053 case NotSupportedError:
2040 message = "The element has no supported sources."; 2054 message = "The element has no supported sources.";
2041 break; 2055 break;
2042 default: 2056 default:
2043 ASSERT_NOT_REACHED(); 2057 ASSERT_NOT_REACHED();
2044 } 2058 }
2045 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(code.get(), message)); 2059 resolver->reject(DOMException::create(code.get(), message));
2060 return promise;
2046 } 2061 }
2047 2062
2048 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
2049 ScriptPromise promise = resolver->promise();
2050
2051 m_playResolvers.append(resolver);
2052 return promise; 2063 return promise;
2053 } 2064 }
2054 2065
2055 Nullable<ExceptionCode> HTMLMediaElement::play() 2066 Nullable<ExceptionCode> HTMLMediaElement::play()
2056 { 2067 {
2057 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")"; 2068 DVLOG(MEDIA_LOG_LEVEL) << "play(" << (void*)this << ")";
2058 2069
2059 m_autoplayHelper->playMethodCalled(); 2070 m_autoplayHelper->playMethodCalled();
2060 2071
2061 if (!UserGestureIndicator::processingUserGesture()) { 2072 if (!UserGestureIndicator::processingUserGesture()) {
(...skipping 1542 matching lines...) Expand 10 before | Expand all | Expand 10 after
3604 visitor->trace(m_asyncEventQueue); 3615 visitor->trace(m_asyncEventQueue);
3605 visitor->trace(m_error); 3616 visitor->trace(m_error);
3606 visitor->trace(m_currentSourceNode); 3617 visitor->trace(m_currentSourceNode);
3607 visitor->trace(m_nextChildNodeToConsider); 3618 visitor->trace(m_nextChildNodeToConsider);
3608 visitor->trace(m_mediaSource); 3619 visitor->trace(m_mediaSource);
3609 visitor->trace(m_audioTracks); 3620 visitor->trace(m_audioTracks);
3610 visitor->trace(m_videoTracks); 3621 visitor->trace(m_videoTracks);
3611 visitor->trace(m_cueTimeline); 3622 visitor->trace(m_cueTimeline);
3612 visitor->trace(m_textTracks); 3623 visitor->trace(m_textTracks);
3613 visitor->trace(m_textTracksWhenResourceSelectionBegan); 3624 visitor->trace(m_textTracksWhenResourceSelectionBegan);
3614 visitor->trace(m_playResolvers); 3625 visitor->trace(m_playPromiseResolvers);
3626 visitor->trace(m_playPromiseResolveList);
3627 visitor->trace(m_playPromiseRejectList);
3615 visitor->trace(m_audioSourceProvider); 3628 visitor->trace(m_audioSourceProvider);
3616 visitor->trace(m_autoplayHelperClient); 3629 visitor->trace(m_autoplayHelperClient);
3617 visitor->trace(m_autoplayHelper); 3630 visitor->trace(m_autoplayHelper);
3618 visitor->trace(m_srcObject); 3631 visitor->trace(m_srcObject);
3619 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this); 3632 visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::c learWeakMembers>(this);
3620 Supplementable<HTMLMediaElement>::trace(visitor); 3633 Supplementable<HTMLMediaElement>::trace(visitor);
3621 HTMLElement::trace(visitor); 3634 HTMLElement::trace(visitor);
3622 ActiveDOMObject::trace(visitor); 3635 ActiveDOMObject::trace(visitor);
3623 } 3636 }
3624 3637
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
3701 // TODO(liberato): remove once autoplay gesture override experiment concludes. 3714 // TODO(liberato): remove once autoplay gesture override experiment concludes.
3702 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() 3715 void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
3703 { 3716 {
3704 if (FrameView* view = document().view()) 3717 if (FrameView* view = document().view())
3705 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea())); 3718 m_autoplayHelper->positionChanged(view->rootFrameToContents(view->comput eVisibleArea()));
3706 m_autoplayHelper->triggerAutoplayViewportCheckForTesting(); 3719 m_autoplayHelper->triggerAutoplayViewportCheckForTesting();
3707 } 3720 }
3708 3721
3709 void HTMLMediaElement::scheduleResolvePlayPromises() 3722 void HTMLMediaElement::scheduleResolvePlayPromises()
3710 { 3723 {
3711 // Per spec, if there are two tasks in the queue, the first task will remove 3724 // TODO(mlamouri): per spec, we should create a new task but we can't create
3712 // all the pending promises making the second task useless unless a promise 3725 // a new cancellable task without cancelling the previous one. There are two
3713 // can be added between the first and second task being run which is not 3726 // approaches then: cancel the previous task and create a new one with the
3714 // possible at the moment. 3727 // appended promise list or append the new promise to the current list. The
3728 // latter approach is preferred because it might be the less observable
3729 // change.
3730 DCHECK(m_playPromiseResolveList.isEmpty() || m_playPromiseResolveTask->isPen ding());
3731 if (m_playPromiseResolvers.isEmpty())
3732 return;
3733
3734 m_playPromiseResolveList.appendVector(m_playPromiseResolvers);
3735 m_playPromiseResolvers.clear();
3736
3715 if (m_playPromiseResolveTask->isPending()) 3737 if (m_playPromiseResolveTask->isPending())
3716 return; 3738 return;
3717 3739
3718 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate()); 3740 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseResolveTask->cancelAndCreate());
3719 } 3741 }
3720 3742
3721 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code) 3743 void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code)
3722 { 3744 {
3723 // Per spec, if there are two tasks in the queue, the first task will remove 3745 // TODO(mlamouri): per spec, we should create a new task but we can't create
3724 // all the pending promises making the second task useless unless a promise 3746 // a new cancellable task without cancelling the previous one. There are two
3725 // can be added between the first and second task being run which is not 3747 // approaches then: cancel the previous task and create a new one with the
3726 // possible at the moment. 3748 // appended promise list or append the new promise to the current list. The
3749 // latter approach is preferred because it might be the less observable
3750 // change.
3751 DCHECK(m_playPromiseRejectList.isEmpty() || m_playPromiseRejectTask->isPendi ng());
3752 if (m_playPromiseResolvers.isEmpty())
3753 return;
3754
3755 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3756 m_playPromiseResolvers.clear();
3757
3727 if (m_playPromiseRejectTask->isPending()) 3758 if (m_playPromiseRejectTask->isPending())
3728 return; 3759 return;
3729 3760
3730 // TODO(mlamouri): because cancellable tasks can't take parameters, the 3761 // TODO(mlamouri): because cancellable tasks can't take parameters, the
3731 // error code needs to be saved. 3762 // error code needs to be saved.
3732 m_playPromiseErrorCode = code; 3763 m_playPromiseErrorCode = code;
3733 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate()); 3764 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO M_HERE, m_playPromiseRejectTask->cancelAndCreate());
3734 } 3765 }
3735 3766
3736 void HTMLMediaElement::scheduleNotifyPlaying() 3767 void HTMLMediaElement::scheduleNotifyPlaying()
3737 { 3768 {
3738 scheduleEvent(EventTypeNames::playing); 3769 scheduleEvent(EventTypeNames::playing);
3739 scheduleResolvePlayPromises(); 3770 scheduleResolvePlayPromises();
3740 } 3771 }
3741 3772
3742 void HTMLMediaElement::resolvePlayPromises() 3773 void HTMLMediaElement::resolveScheduledPlayPromises()
3743 { 3774 {
3744 for (auto& resolver: m_playResolvers) 3775 for (auto& resolver: m_playPromiseResolveList)
3745 resolver->resolve(); 3776 resolver->resolve();
3746 3777
3747 m_playResolvers.clear(); 3778 m_playPromiseResolveList.clear();
3748 } 3779 }
3749 3780
3750 void HTMLMediaElement::rejectPlayPromises() 3781 void HTMLMediaElement::rejectScheduledPlayPromises()
3751 { 3782 {
3752 // TODO(mlamouri): the message is generated based on the code because 3783 // TODO(mlamouri): the message is generated based on the code because
3753 // arguments can't be passed to a cancellable task. In order to save space 3784 // arguments can't be passed to a cancellable task. In order to save space
3754 // used by the object, the string isn't saved. 3785 // used by the object, the string isn't saved.
3755 ASSERT(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError); 3786 DCHECK(m_playPromiseErrorCode == AbortError || m_playPromiseErrorCode == Not SupportedError);
3756 if (m_playPromiseErrorCode == AbortError) 3787 if (m_playPromiseErrorCode == AbortError)
3757 rejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause()."); 3788 rejectPlayPromisesInternal(AbortError, "The play() request was interrupt ed by a call to pause().");
3758 else 3789 else
3759 rejectPlayPromises(NotSupportedError, "Failed to load because no support ed source was found."); 3790 rejectPlayPromisesInternal(NotSupportedError, "Failed to load because no supported source was found.");
3760 } 3791 }
3761 3792
3762 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age) 3793 void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& mess age)
3763 { 3794 {
3764 ASSERT(code == AbortError || code == NotSupportedError); 3795 m_playPromiseRejectList.appendVector(m_playPromiseResolvers);
3796 m_playPromiseResolvers.clear();
3797 rejectPlayPromisesInternal(code, message);
3798 }
3765 3799
3766 for (auto& resolver: m_playResolvers) 3800 void HTMLMediaElement::rejectPlayPromisesInternal(ExceptionCode code, const Stri ng& message)
3801 {
3802 DCHECK(code == AbortError || code == NotSupportedError);
3803
3804 for (auto& resolver: m_playPromiseRejectList)
3767 resolver->reject(DOMException::create(code, message)); 3805 resolver->reject(DOMException::create(code, message));
3768 3806
3769 m_playResolvers.clear(); 3807 m_playPromiseRejectList.clear();
3770 } 3808 }
3771 3809
3772 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const 3810 EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const
3773 { 3811 {
3774 if (isHTMLVideoElement()) { 3812 if (isHTMLVideoElement()) {
3775 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax)); 3813 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Sh ow.Video", MediaControlsShowMax));
3776 return histogram; 3814 return histogram;
3777 } 3815 }
3778 3816
3779 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax)); 3817 DEFINE_STATIC_LOCAL(EnumerationHistogram, histogram, ("Media.Controls.Show.A udio", MediaControlsShowMax));
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
3905 3943
3906 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst 3944 IntRect HTMLMediaElement::AutoplayHelperClientImpl::absoluteBoundingBoxRect() co nst
3907 { 3945 {
3908 IntRect result; 3946 IntRect result;
3909 if (LayoutObject* object = m_element->layoutObject()) 3947 if (LayoutObject* object = m_element->layoutObject())
3910 result = object->absoluteBoundingBoxRect(); 3948 result = object->absoluteBoundingBoxRect();
3911 return result; 3949 return result;
3912 } 3950 }
3913 3951
3914 } // namespace blink 3952 } // 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