Index: third_party/WebKit/Source/core/html/media/AutoplayPolicy.cpp |
diff --git a/third_party/WebKit/Source/core/html/media/AutoplayPolicy.cpp b/third_party/WebKit/Source/core/html/media/AutoplayPolicy.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..256bad5bc30d30f08685546789589065c5079908 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/html/media/AutoplayPolicy.cpp |
@@ -0,0 +1,295 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "core/html/media/AutoplayPolicy.h" |
+ |
+#include "core/dom/Document.h" |
+#include "core/dom/ElementVisibilityObserver.h" |
+#include "core/frame/ContentSettingsClient.h" |
+#include "core/frame/LocalFrame.h" |
+#include "core/frame/Settings.h" |
+#include "core/html/HTMLMediaElement.h" |
+#include "core/html/media/AutoplayUmaHelper.h" |
+#include "platform/RuntimeEnabledFeatures.h" |
+#include "platform/UserGestureIndicator.h" |
+#include "public/platform/WebMediaPlayer.h" |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+bool IsDocumentCrossOrigin(Document& document) { |
+ const LocalFrame* frame = document.GetFrame(); |
+ return frame && frame->IsCrossOriginSubframe(); |
+} |
+ |
+// Returns whether |document| is whitelisted for autoplay. If true, the user |
+// gesture lock will be initilized as false, indicating that the element is |
+// allowed to autoplay unmuted without user gesture. |
+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(); |
+} |
+ |
+} // anonymous namespace |
+ |
+AutoplayPolicy::AutoplayPolicy(HTMLMediaElement* element) |
+ : locked_pending_user_gesture_(false), |
+ locked_pending_user_gesture_if_cross_origin_experiment_enabled_(true), |
+ element_(element), |
+ autoplay_visibility_observer_(nullptr), |
+ autoplay_uma_helper_(AutoplayUmaHelper::Create(element)) { |
+ locked_pending_user_gesture_ = |
+ ComputeLockedPendingUserGesture(element->GetDocument()); |
+ locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = |
+ IsDocumentCrossOrigin(element->GetDocument()); |
+} |
+ |
+void AutoplayPolicy::VideoWillBeDrawnToCanvas() const { |
+ autoplay_uma_helper_->VideoWillBeDrawnToCanvas(); |
+} |
+ |
+void AutoplayPolicy::DidMoveToNewDocument(Document& 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(element_->GetDocument()); |
+ if (new_document_requires_user_gesture && !old_document_requires_user_gesture) |
+ locked_pending_user_gesture_ = true; |
+ |
+ if (IsDocumentCrossOrigin(element_->GetDocument()) && |
+ !IsDocumentCrossOrigin(old_document)) |
+ locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = true; |
+ |
+ autoplay_uma_helper_->DidMoveToNewDocument(old_document); |
+} |
+ |
+bool AutoplayPolicy::IsEligibleForAutoplayMuted() const { |
+ return element_->IsHTMLVideoElement() && element_->muted() && |
+ RuntimeEnabledFeatures::autoplayMutedVideosEnabled(); |
+} |
+ |
+void AutoplayPolicy::StartAutoplayMutedWhenVisible() { |
+ // 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_) |
+ return; |
+ |
+ autoplay_visibility_observer_ = new ElementVisibilityObserver( |
+ element_, WTF::Bind(&AutoplayPolicy::OnVisibilityChangedForAutoplay, |
+ WrapWeakPersistent(this))); |
+ autoplay_visibility_observer_->Start(); |
+} |
+ |
+void AutoplayPolicy::StopAutoplayMutedWhenVisible() { |
+ if (!autoplay_visibility_observer_) |
+ return; |
+ |
+ autoplay_visibility_observer_->Stop(); |
+ autoplay_visibility_observer_ = nullptr; |
+} |
+ |
+bool AutoplayPolicy::RequestAutoplayUnmute() { |
+ bool was_autoplaying_muted = IsAutoplayingMuted(); |
Zhiqiang Zhang (Slow)
2017/04/20 15:36:26
I found why the tests are broken...
Actually the
|
+ |
+ TryUnlockingUserGesture(); |
+ |
+ if (was_autoplaying_muted) { |
+ if (IsGestureNeededForPlayback()) { |
+ autoplay_uma_helper_->RecordAutoplayUnmuteStatus( |
+ AutoplayUnmuteActionStatus::kFailure); |
+ return false; |
+ } |
+ autoplay_uma_helper_->RecordAutoplayUnmuteStatus( |
+ AutoplayUnmuteActionStatus::kSuccess); |
+ } |
+ return true; |
+} |
+ |
+bool AutoplayPolicy::RequestAutoplayByAttribute() { |
+ if (!ShouldAutoplay()) |
+ return false; |
+ |
+ autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kAttribute); |
+ |
+ if (IsGestureNeededForPlayback()) { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayBlocked); |
+ return false; |
+ } |
+ |
+ if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayBlocked); |
+ } else { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayAllowed); |
+ } |
+ |
+ if (!IsEligibleForAutoplayMuted()) |
mlamouri (slow - plz ping)
2017/04/20 14:37:27
Maybe add a comment reminding the user that this o
Zhiqiang Zhang (Slow)
2017/04/20 15:36:26
Done.
|
+ return true; |
+ |
+ // Autoplay muted video should be handled by AutoplayPolicy based on the |
+ // visibily. |
+ StartAutoplayMutedWhenVisible(); |
+ return false; |
+} |
+ |
+Nullable<ExceptionCode> AutoplayPolicy::RequestPlay() { |
+ if (!UserGestureIndicator::ProcessingUserGesture()) { |
+ autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kMethod); |
+ if (IsGestureNeededForPlayback()) { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayBlocked); |
+ return kNotAllowedError; |
+ } |
+ |
+ if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayBlocked); |
+ } else { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kAutoplayAllowed); |
+ } |
+ } else { |
+ autoplay_uma_helper_->RecordCrossOriginAutoplayResult( |
+ CrossOriginAutoplayResult::kPlayedWithGesture); |
+ TryUnlockingUserGesture(); |
+ } |
+ |
+ return nullptr; |
+} |
+ |
+bool AutoplayPolicy::IsAutoplayingMuted() const { |
+ if (!element_->IsHTMLVideoElement() || |
+ !RuntimeEnabledFeatures::autoplayMutedVideosEnabled()) { |
+ return false; |
+ } |
+ |
+ return !element_->paused() && element_->muted() && |
+ IsLockedPendingUserGesture(); |
+} |
+ |
+bool AutoplayPolicy::IsLockedPendingUserGesture() const { |
+ return locked_pending_user_gesture_; |
+} |
+ |
+void AutoplayPolicy::TryUnlockingUserGesture() { |
+ if (IsLockedPendingUserGesture() && |
+ UserGestureIndicator::UtilizeUserGesture()) { |
+ UnlockUserGesture(); |
+ } |
+} |
+ |
+void AutoplayPolicy::UnlockUserGesture() { |
+ locked_pending_user_gesture_ = false; |
+ locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = false; |
+} |
+ |
+bool AutoplayPolicy::IsGestureNeededForPlayback() const { |
+ if (!locked_pending_user_gesture_) |
+ return false; |
+ |
+ return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked(); |
+} |
+ |
+bool AutoplayPolicy::IsGestureNeededForPlaybackIfPendingUserGestureIsLocked() |
+ const { |
+ if (element_->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 (element_->IsHTMLVideoElement() && element_->muted() && |
+ RuntimeEnabledFeatures::autoplayMutedVideosEnabled() && |
+ !(element_->GetDocument().GetSettings() && |
+ element_->GetDocument().GetSettings()->GetDataSaverEnabled()) && |
+ !(element_->GetDocument().GetSettings() && |
+ element_->GetDocument() |
+ .GetSettings() |
+ ->GetForcePreloadNoneForMediaElements()) && |
+ IsAutoplayAllowedPerSettings()) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+void AutoplayPolicy::OnVisibilityChangedForAutoplay(bool is_visible) { |
+ if (!is_visible) { |
+ if (element_->can_autoplay_ && element_->Autoplay()) { |
+ element_->PauseInternal(); |
+ element_->can_autoplay_ = true; |
+ } |
+ return; |
+ } |
+ |
+ if (ShouldAutoplay()) { |
+ element_->paused_ = false; |
+ element_->ScheduleEvent(EventTypeNames::play); |
+ element_->ScheduleNotifyPlaying(); |
+ |
+ element_->UpdatePlayState(); |
+ } |
+} |
+ |
+bool AutoplayPolicy::IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled() |
+ const { |
+ if (!locked_pending_user_gesture_if_cross_origin_experiment_enabled_) |
+ return false; |
+ |
+ return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked(); |
+} |
+ |
+bool AutoplayPolicy::IsAutoplayAllowedPerSettings() const { |
+ LocalFrame* frame = element_->GetDocument().GetFrame(); |
+ if (!frame) |
+ return false; |
+ return frame->GetContentSettingsClient()->AllowAutoplay(true); |
+} |
+ |
+bool AutoplayPolicy::ShouldAutoplay() { |
+ if (element_->GetDocument().IsSandboxed(kSandboxAutomaticFeatures)) |
+ return false; |
+ return element_->can_autoplay_ && element_->paused_ && element_->Autoplay(); |
+} |
+ |
+DEFINE_TRACE(AutoplayPolicy) { |
+ visitor->Trace(element_); |
+ visitor->Trace(autoplay_visibility_observer_); |
+ visitor->Trace(autoplay_uma_helper_); |
+} |
+ |
+} // namespace blink |