Chromium Code Reviews| 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 d3e0fc4ebe09e239b56f59bda8002f00ea4d79a5..3a1b71ec68a2d1a2a740b28fd61e53dd9895b4b5 100644 |
| --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| @@ -38,12 +38,10 @@ |
| #include "core/dom/DOMException.h" |
| #include "core/dom/DocumentUserGestureToken.h" |
| #include "core/dom/ElementTraversal.h" |
| -#include "core/dom/ElementVisibilityObserver.h" |
| #include "core/dom/Fullscreen.h" |
| #include "core/dom/TaskRunnerHelper.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/events/Event.h" |
| -#include "core/frame/ContentSettingsClient.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/LocalFrameClient.h" |
| @@ -53,6 +51,7 @@ |
| #include "core/html/HTMLSourceElement.h" |
| #include "core/html/HTMLTrackElement.h" |
| #include "core/html/TimeRanges.h" |
| +#include "core/html/media/AutoplayPolicy.h" |
| #include "core/html/media/AutoplayUmaHelper.h" |
| #include "core/html/media/HTMLMediaElementControlsList.h" |
| #include "core/html/media/HTMLMediaSource.h" |
| @@ -333,41 +332,6 @@ String PreloadTypeToString(WebMediaPlayer::Preload preload_type) { |
| return String(); |
| } |
| -bool IsDocumentCrossOrigin(Document& document) { |
| - const LocalFrame* frame = document.GetFrame(); |
| - return frame && frame->IsCrossOriginSubframe(); |
| -} |
| - |
| -bool IsDocumentWhitelisted(Document& document) { |
| - DCHECK(document.GetSettings()); |
| - |
| - const String& whitelist_scope = |
| - document.GetSettings()->GetMediaPlaybackGestureWhitelistScope(); |
| - if (whitelist_scope.IsNull() || whitelist_scope.IsEmpty()) |
| - return false; |
| - |
| - return document.Url().GetString().StartsWith(whitelist_scope); |
| -} |
| - |
| -// Return true if and only if the document settings specifies media playback |
| -// requires user gesture. |
| -bool ComputeLockedPendingUserGesture(Document& document) { |
| - if (!document.GetSettings()) |
| - return false; |
| - |
| - if (IsDocumentWhitelisted(document)) { |
| - return false; |
| - } |
| - |
| - if (document.GetSettings() |
| - ->GetCrossOriginMediaPlaybackRequiresUserGesture() && |
| - IsDocumentCrossOrigin(document)) { |
| - return true; |
| - } |
| - |
| - return document.GetSettings()->GetMediaPlaybackRequiresUserGesture(); |
| -} |
| - |
| std::unique_ptr<MediaControls::Factory>& MediaControlsFactory() { |
| DEFINE_STATIC_LOCAL(std::unique_ptr<MediaControls::Factory>, |
| media_controls_factory, ()); |
| @@ -505,8 +469,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tag_name, |
| official_playback_position_needs_update_(true), |
| fragment_end_time_(std::numeric_limits<double>::quiet_NaN()), |
| pending_action_flags_(0), |
| - locked_pending_user_gesture_(false), |
| - locked_pending_user_gesture_if_cross_origin_experiment_enabled_(true), |
| playing_(false), |
| should_delay_load_event_(false), |
| have_fired_loaded_data_(false), |
| @@ -527,17 +489,13 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tag_name, |
| video_tracks_(this, VideoTrackList::Create(*this)), |
| text_tracks_(this, nullptr), |
| audio_source_node_(nullptr), |
| + autoplay_policy_(new AutoplayPolicy(this)), |
| autoplay_uma_helper_(AutoplayUmaHelper::Create(this)), |
| remote_playback_client_(nullptr), |
| - autoplay_visibility_observer_(nullptr), |
| media_controls_(nullptr), |
| controls_list_(HTMLMediaElementControlsList::Create(this)) { |
| BLINK_MEDIA_LOG << "HTMLMediaElement(" << (void*)this << ")"; |
| - locked_pending_user_gesture_ = ComputeLockedPendingUserGesture(document); |
| - locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = |
| - IsDocumentCrossOrigin(document); |
| - |
| LocalFrame* frame = document.GetFrame(); |
| if (frame) { |
| remote_playback_client_ = |
| @@ -592,14 +550,7 @@ void HTMLMediaElement::DidMoveToNewDocument(Document& old_document) { |
| TaskRunnerHelper::Get(TaskType::kUnthrottled, &GetDocument())); |
| autoplay_uma_helper_->DidMoveToNewDocument(old_document); |
| - // If any experiment is enabled, then we want to enable a user gesture by |
| - // default, otherwise the experiment does nothing. |
| - bool old_document_requires_user_gesture = |
| - ComputeLockedPendingUserGesture(old_document); |
| - bool new_document_requires_user_gesture = |
| - ComputeLockedPendingUserGesture(GetDocument()); |
| - if (new_document_requires_user_gesture && !old_document_requires_user_gesture) |
| - locked_pending_user_gesture_ = true; |
| + autoplay_policy_->DidMoveToNewDocument(old_document); |
| if (should_delay_load_event_) { |
| GetDocument().IncrementLoadEventDelayCount(); |
| @@ -612,10 +563,6 @@ void HTMLMediaElement::DidMoveToNewDocument(Document& old_document) { |
| old_document.IncrementLoadEventDelayCount(); |
| } |
| - if (IsDocumentCrossOrigin(GetDocument()) && |
| - !IsDocumentCrossOrigin(old_document)) |
| - locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = true; |
| - |
| RemoveElementFromDocumentMap(this, &old_document); |
| AddElementToDocumentMap(this, &GetDocument()); |
| @@ -845,10 +792,7 @@ String HTMLMediaElement::canPlayType(const String& mime_type) const { |
| void HTMLMediaElement::load() { |
| BLINK_MEDIA_LOG << "load(" << (void*)this << ")"; |
| - if (IsLockedPendingUserGesture() && |
| - UserGestureIndicator::UtilizeUserGesture()) { |
| - UnlockUserGesture(); |
| - } |
| + autoplay_policy_->TryUnlockingUserGesture(); |
| ignore_preload_none_ = true; |
| InvokeLoadAlgorithm(); |
| @@ -1481,10 +1425,6 @@ bool HTMLMediaElement::IsMediaDataCORSSameOrigin(SecurityOrigin* origin) const { |
| !origin->TaintsCanvas(currentSrc())); |
| } |
| -bool HTMLMediaElement::IsInCrossOriginFrame() const { |
| - return IsDocumentCrossOrigin(GetDocument()); |
| -} |
| - |
| void HTMLMediaElement::StartProgressEventTimer() { |
| if (progress_event_timer_.IsActive()) |
| return; |
| @@ -1843,26 +1783,17 @@ void HTMLMediaElement::SetReadyState(ReadyState state) { |
| if (ShouldAutoplay()) { |
| autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kAttribute); |
| - if (!IsGestureNeededForPlayback()) { |
| - if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) { |
| + if (!autoplay_policy_->IsGestureNeededForPlayback()) { |
| + if (autoplay_policy_ |
| + ->IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) { |
| autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| CrossOriginAutoplayResult::kAutoplayBlocked); |
| } else { |
| autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| CrossOriginAutoplayResult::kAutoplayAllowed); |
| } |
| - if (IsHTMLVideoElement() && muted() && |
| - RuntimeEnabledFeatures::autoplayMutedVideosEnabled()) { |
| - // We might end up in a situation where the previous |
| - // observer didn't had time to fire yet. We can avoid |
| - // creating a new one in this case. |
| - if (!autoplay_visibility_observer_) { |
| - autoplay_visibility_observer_ = new ElementVisibilityObserver( |
| - this, |
| - WTF::Bind(&HTMLMediaElement::OnVisibilityChangedForAutoplay, |
| - WrapWeakPersistent(this))); |
| - autoplay_visibility_observer_->Start(); |
| - } |
| + if (autoplay_policy_->IsEligibleForAutoplayMuted()) { |
| + autoplay_policy_->StartAutoplayMutedWhenVisible(); |
| } else { |
| paused_ = false; |
| ScheduleEvent(EventTypeNames::play); |
| @@ -2283,7 +2214,7 @@ String HTMLMediaElement::EffectivePreload() const { |
| } |
| WebMediaPlayer::Preload HTMLMediaElement::EffectivePreloadType() const { |
| - if (Autoplay() && !IsGestureNeededForPlayback()) |
| + if (Autoplay() && !autoplay_policy_->IsGestureNeededForPlayback()) |
| return WebMediaPlayer::kPreloadAuto; |
| WebMediaPlayer::Preload preload = PreloadType(); |
| @@ -2329,48 +2260,34 @@ ScriptPromise HTMLMediaElement::playForBindings(ScriptState* script_state) { |
| Nullable<ExceptionCode> HTMLMediaElement::Play() { |
| BLINK_MEDIA_LOG << "play(" << (void*)this << ")"; |
| - if (!UserGestureIndicator::ProcessingUserGesture()) { |
| - autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kMethod); |
| - if (IsGestureNeededForPlayback()) { |
| - // If we're already playing, then this play would do nothing anyway. |
| - // Call playInternal to handle scheduling the promise resolution. |
| - if (!paused_) { |
| - PlayInternal(); |
| - return nullptr; |
| - } |
| - |
| - autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| - CrossOriginAutoplayResult::kAutoplayBlocked); |
| - String message = ExceptionMessages::FailedToExecute( |
| - "play", "HTMLMediaElement", |
| - "API can only be initiated by a user gesture."); |
| - GetDocument().AddConsoleMessage(ConsoleMessage::Create( |
| - kJSMessageSource, kWarningMessageLevel, message)); |
| - return kNotAllowedError; |
| - } else { |
| - if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) { |
| - autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| - CrossOriginAutoplayResult::kAutoplayBlocked); |
| - } else { |
| - autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| - CrossOriginAutoplayResult::kAutoplayAllowed); |
| - } |
| - } |
| - } else { |
| - autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
| - CrossOriginAutoplayResult::kPlayedWithGesture); |
| - UserGestureIndicator::UtilizeUserGesture(); |
| - UnlockUserGesture(); |
| + // If we're already playing, then this play would do nothing anyway. |
| + // Call playInternal to handle scheduling the promise resolution. |
| + if (!paused_) { |
| + PlayInternal(); |
| + return nullptr; |
| } |
|
mlamouri (slow - plz ping)
2017/04/18 13:28:28
The logic is slightly changing with this change. I
Zhiqiang Zhang (Slow)
2017/04/18 16:56:44
OK, I moved the code a bit so it should have the s
|
| + autoplay_policy_->StopAutoplayMutedWhenVisible(); |
| + |
| if (error_ && error_->code() == MediaError::kMediaErrSrcNotSupported) |
| return kNotSupportedError; |
| - if (autoplay_visibility_observer_) { |
| - autoplay_visibility_observer_->Stop(); |
| - autoplay_visibility_observer_ = nullptr; |
| + // The only place to call IsAutoplayAllowedFromPlayMethod(), don't call from |
| + // anywhere else! |
|
mlamouri (slow - plz ping)
2017/04/18 13:28:28
Comment is out of date, right?
Zhiqiang Zhang (Slow)
2017/04/18 16:56:44
Done.
|
| + Nullable<ExceptionCode> exception_code = |
| + autoplay_policy_->CheckPlayMethodAllowed(); |
| + |
| + if (exception_code == kNotAllowedError) { |
| + String message = ExceptionMessages::FailedToExecute( |
| + "play", "HTMLMediaElement", |
| + "API can only be initiated by a user gesture."); |
| + GetDocument().AddConsoleMessage(ConsoleMessage::Create( |
| + kJSMessageSource, kWarningMessageLevel, message)); |
| + return exception_code; |
| } |
| + DCHECK(exception_code.IsNull()); |
| + |
| PlayInternal(); |
| return nullptr; |
| @@ -2410,11 +2327,7 @@ void HTMLMediaElement::PlayInternal() { |
| void HTMLMediaElement::pause() { |
| BLINK_MEDIA_LOG << "pause(" << (void*)this << ")"; |
| - if (autoplay_visibility_observer_) { |
| - autoplay_visibility_observer_->Stop(); |
| - autoplay_visibility_observer_ = nullptr; |
| - } |
| - |
| + autoplay_policy_->StopAutoplayMutedWhenVisible(); |
| PauseInternal(); |
| } |
| @@ -2562,41 +2475,26 @@ void HTMLMediaElement::setMuted(bool muted) { |
| if (muted_ == muted) |
| return; |
| - bool was_autoplaying_muted = IsAutoplayingMuted(); |
| - bool was_pending_autoplay_muted = autoplay_visibility_observer_ && paused() && |
| - muted_ && IsLockedPendingUserGesture(); |
| - |
| + // TODO(zqzhang): use TryUnlockingUserGesture? |
| if (UserGestureIndicator::ProcessingUserGesture()) |
| - UnlockUserGesture(); |
| + autoplay_policy_->UnlockUserGesture(); |
| + |
| + bool should_pause_after_unmute = |
| + autoplay_policy_->CheckUnmuteShouldPauseAutoplay(); |
|
mlamouri (slow - plz ping)
2017/04/18 13:28:28
Not sure this is at the right place. Should this b
Zhiqiang Zhang (Slow)
2017/04/18 16:56:44
The logic won't change actually.
The original logi
|
| muted_ = muted; |
| ScheduleEvent(EventTypeNames::volumechange); |
| - // If an element autoplayed while muted, it needs to be unlocked to unmute, |
| - // otherwise, it will be paused. |
| - if (was_autoplaying_muted) { |
| - if (IsGestureNeededForPlayback()) { |
| - pause(); |
| - autoplay_uma_helper_->RecordAutoplayUnmuteStatus( |
| - AutoplayUnmuteActionStatus::kFailure); |
| - } else { |
| - autoplay_uma_helper_->RecordAutoplayUnmuteStatus( |
| - AutoplayUnmuteActionStatus::kSuccess); |
| - } |
| - } |
| + if (should_pause_after_unmute) |
| + pause(); |
| // This is called after the volumechange event to make sure isAutoplayingMuted |
| // returns the right value when webMediaPlayer receives the volume update. |
| if (GetWebMediaPlayer()) |
| GetWebMediaPlayer()->SetVolume(EffectiveMediaVolume()); |
| - // If an element was a candidate for autoplay muted but not visible, it will |
| - // have a visibility observer ready to start its playback. |
| - if (was_pending_autoplay_muted) { |
| - autoplay_visibility_observer_->Stop(); |
| - autoplay_visibility_observer_ = nullptr; |
| - } |
| + autoplay_policy_->StopAutoplayMutedWhenVisible(); |
| } |
| double HTMLMediaElement::EffectiveMediaVolume() const { |
| @@ -3318,15 +3216,6 @@ WebMediaPlayer::TrackId HTMLMediaElement::GetSelectedVideoTrackId() { |
| return track->id(); |
| } |
| -bool HTMLMediaElement::IsAutoplayingMuted() { |
| - if (!IsHTMLVideoElement() || |
| - !RuntimeEnabledFeatures::autoplayMutedVideosEnabled()) { |
| - return false; |
| - } |
| - |
| - return !paused() && muted() && IsLockedPendingUserGesture(); |
| -} |
| - |
| void HTMLMediaElement::RequestReload(const WebURL& new_url) { |
| DCHECK(GetWebMediaPlayer()); |
| DCHECK(!src_object_); |
| @@ -3336,6 +3225,10 @@ void HTMLMediaElement::RequestReload(const WebURL& new_url) { |
| StartPlayerLoad(new_url); |
| } |
| +bool HTMLMediaElement::IsAutoplayingMuted() { |
| + return autoplay_policy_->IsAutoplayingMuted(); |
| +} |
| + |
| // MediaPlayerPresentation methods |
| void HTMLMediaElement::Repaint() { |
| if (web_layer_) |
| @@ -3933,7 +3826,7 @@ DEFINE_TRACE(HTMLMediaElement) { |
| visitor->Trace(audio_source_provider_); |
| visitor->Trace(autoplay_uma_helper_); |
| visitor->Trace(src_object_); |
| - visitor->Trace(autoplay_visibility_observer_); |
| + visitor->Trace(autoplay_policy_); |
| visitor->Trace(media_controls_); |
| visitor->Trace(controls_list_); |
| visitor->template RegisterWeakMembers<HTMLMediaElement, |
| @@ -3983,60 +3876,6 @@ void HTMLMediaElement::SelectInitialTracksIfNecessary() { |
| videoTracks().AnonymousIndexedGetter(0)->setSelected(true); |
| } |
| -bool HTMLMediaElement::IsLockedPendingUserGesture() const { |
| - return locked_pending_user_gesture_; |
| -} |
| - |
| -void HTMLMediaElement::UnlockUserGesture() { |
| - locked_pending_user_gesture_ = false; |
| - locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = false; |
| -} |
| - |
| -bool HTMLMediaElement::IsGestureNeededForPlayback() const { |
| - if (!locked_pending_user_gesture_) |
| - return false; |
| - |
| - return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked(); |
| -} |
| - |
| -bool HTMLMediaElement:: |
| - IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled() const { |
| - if (!locked_pending_user_gesture_if_cross_origin_experiment_enabled_) |
| - return false; |
| - |
| - return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked(); |
| -} |
| - |
| -bool HTMLMediaElement::IsGestureNeededForPlaybackIfPendingUserGestureIsLocked() |
| - const { |
| - if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) |
| - return false; |
| - |
| - // We want to allow muted video to autoplay if: |
| - // - the flag is enabled; |
| - // - Data Saver is not enabled; |
| - // - Preload was not disabled (low end devices); |
| - // - Autoplay is enabled in settings; |
| - if (IsHTMLVideoElement() && muted() && |
| - RuntimeEnabledFeatures::autoplayMutedVideosEnabled() && |
| - !(GetDocument().GetSettings() && |
| - GetDocument().GetSettings()->GetDataSaverEnabled()) && |
| - !(GetDocument().GetSettings() && |
| - GetDocument().GetSettings()->GetForcePreloadNoneForMediaElements()) && |
| - IsAutoplayAllowedPerSettings()) { |
| - return false; |
| - } |
| - |
| - return true; |
| -} |
| - |
| -bool HTMLMediaElement::IsAutoplayAllowedPerSettings() const { |
| - LocalFrame* frame = GetDocument().GetFrame(); |
| - if (!frame) |
| - return false; |
| - return frame->GetContentSettingsClient()->AllowAutoplay(true); |
| -} |
| - |
| void HTMLMediaElement::SetNetworkState(NetworkState state) { |
| if (network_state_ == state) |
| return; |
| @@ -4166,24 +4005,6 @@ EnumerationHistogram& HTMLMediaElement::ShowControlsHistogram() const { |
| return histogram; |
| } |
| -void HTMLMediaElement::OnVisibilityChangedForAutoplay(bool is_visible) { |
| - if (!is_visible) { |
| - if (can_autoplay_ && Autoplay()) { |
| - PauseInternal(); |
| - can_autoplay_ = true; |
| - } |
| - return; |
| - } |
| - |
| - if (ShouldAutoplay()) { |
| - paused_ = false; |
| - ScheduleEvent(EventTypeNames::play); |
| - ScheduleNotifyPlaying(); |
| - |
| - UpdatePlayState(); |
| - } |
| -} |
| - |
| void HTMLMediaElement::ClearWeakMembers(Visitor* visitor) { |
| if (!ThreadHeap::IsHeapObjectAlive(audio_source_node_)) { |
| GetAudioSourceProvider().SetClient(nullptr); |