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

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

Issue 2813303005: [Blink>Media] Moving autoplay logic to AutoplayPolicy (Closed)
Patch Set: addressed nits Created 3 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
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/html/media/AutoplayPolicy.h"
6
7 #include "core/dom/Document.h"
8 #include "core/dom/ElementVisibilityObserver.h"
9 #include "core/frame/ContentSettingsClient.h"
10 #include "core/frame/LocalFrame.h"
11 #include "core/frame/Settings.h"
12 #include "core/html/HTMLMediaElement.h"
13 #include "core/html/media/AutoplayUmaHelper.h"
14 #include "platform/RuntimeEnabledFeatures.h"
15 #include "platform/UserGestureIndicator.h"
16 #include "public/platform/WebMediaPlayer.h"
17
18 namespace blink {
19
20 namespace {
21
22 bool IsDocumentCrossOrigin(Document& document) {
23 const LocalFrame* frame = document.GetFrame();
24 return frame && frame->IsCrossOriginSubframe();
25 }
26
27 // Returns whether |document| is whitelisted for autoplay. If true, the user
28 // gesture lock will be initilized as false, indicating that the element is
29 // allowed to autoplay unmuted without user gesture.
30 bool IsDocumentWhitelisted(Document& document) {
31 DCHECK(document.GetSettings());
32
33 const String& whitelist_scope =
34 document.GetSettings()->GetMediaPlaybackGestureWhitelistScope();
35 if (whitelist_scope.IsNull() || whitelist_scope.IsEmpty())
36 return false;
37
38 return document.Url().GetString().StartsWith(whitelist_scope);
39 }
40
41 // Return true if and only if the document settings specifies media playback
42 // requires user gesture.
43 bool ComputeLockedPendingUserGesture(Document& document) {
44 if (!document.GetSettings())
45 return false;
46
47 if (IsDocumentWhitelisted(document)) {
48 return false;
49 }
50
51 if (document.GetSettings()
52 ->GetCrossOriginMediaPlaybackRequiresUserGesture() &&
53 IsDocumentCrossOrigin(document)) {
54 return true;
55 }
56
57 return document.GetSettings()->GetMediaPlaybackRequiresUserGesture();
58 }
59
60 } // anonymous namespace
61
62 AutoplayPolicy::AutoplayPolicy(HTMLMediaElement* element)
63 : locked_pending_user_gesture_(false),
64 locked_pending_user_gesture_if_cross_origin_experiment_enabled_(true),
65 element_(element),
66 autoplay_visibility_observer_(nullptr),
67 autoplay_uma_helper_(AutoplayUmaHelper::Create(element)) {
68 locked_pending_user_gesture_ =
69 ComputeLockedPendingUserGesture(element->GetDocument());
70 locked_pending_user_gesture_if_cross_origin_experiment_enabled_ =
71 IsDocumentCrossOrigin(element->GetDocument());
72 }
73
74 void AutoplayPolicy::VideoWillBeDrawnToCanvas() const {
75 autoplay_uma_helper_->VideoWillBeDrawnToCanvas();
76 }
77
78 void AutoplayPolicy::DidMoveToNewDocument(Document& old_document) {
79 // If any experiment is enabled, then we want to enable a user gesture by
80 // default, otherwise the experiment does nothing.
81 bool old_document_requires_user_gesture =
82 ComputeLockedPendingUserGesture(old_document);
83 bool new_document_requires_user_gesture =
84 ComputeLockedPendingUserGesture(element_->GetDocument());
85 if (new_document_requires_user_gesture && !old_document_requires_user_gesture)
86 locked_pending_user_gesture_ = true;
87
88 if (IsDocumentCrossOrigin(element_->GetDocument()) &&
89 !IsDocumentCrossOrigin(old_document))
90 locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = true;
91
92 autoplay_uma_helper_->DidMoveToNewDocument(old_document);
93 }
94
95 bool AutoplayPolicy::IsEligibleForAutoplayMuted() const {
96 return element_->IsHTMLVideoElement() && element_->muted() &&
97 RuntimeEnabledFeatures::autoplayMutedVideosEnabled();
98 }
99
100 void AutoplayPolicy::StartAutoplayMutedWhenVisible() {
101 // We might end up in a situation where the previous
102 // observer didn't had time to fire yet. We can avoid
103 // creating a new one in this case.
104 if (autoplay_visibility_observer_)
105 return;
106
107 autoplay_visibility_observer_ = new ElementVisibilityObserver(
108 element_, WTF::Bind(&AutoplayPolicy::OnVisibilityChangedForAutoplay,
109 WrapWeakPersistent(this)));
110 autoplay_visibility_observer_->Start();
111 }
112
113 void AutoplayPolicy::StopAutoplayMutedWhenVisible() {
114 if (!autoplay_visibility_observer_)
115 return;
116
117 autoplay_visibility_observer_->Stop();
118 autoplay_visibility_observer_ = nullptr;
119 }
120
121 bool AutoplayPolicy::RequestAutoplayUnmute() {
122 DCHECK(!element_->muted());
123 bool was_autoplaying_muted = IsAutoplayingMutedInternal(true);
124
125 TryUnlockingUserGesture();
126
127 if (was_autoplaying_muted) {
128 if (IsGestureNeededForPlayback()) {
129 autoplay_uma_helper_->RecordAutoplayUnmuteStatus(
130 AutoplayUnmuteActionStatus::kFailure);
131 return false;
132 }
133 autoplay_uma_helper_->RecordAutoplayUnmuteStatus(
134 AutoplayUnmuteActionStatus::kSuccess);
135 }
136 return true;
137 }
138
139 bool AutoplayPolicy::RequestAutoplayByAttribute() {
140 if (!ShouldAutoplay())
141 return false;
142
143 autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kAttribute);
144
145 if (IsGestureNeededForPlayback()) {
146 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
147 CrossOriginAutoplayResult::kAutoplayBlocked);
148 return false;
149 }
150
151 if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) {
152 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
153 CrossOriginAutoplayResult::kAutoplayBlocked);
154 } else {
155 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
156 CrossOriginAutoplayResult::kAutoplayAllowed);
157 }
158
159 // At this point the gesture is not needed for playback per the if statement
160 // above.
161 if (!IsEligibleForAutoplayMuted())
162 return true;
163
164 // Autoplay muted video should be handled by AutoplayPolicy based on the
165 // visibily.
166 StartAutoplayMutedWhenVisible();
167 return false;
168 }
169
170 Nullable<ExceptionCode> AutoplayPolicy::RequestPlay() {
171 if (!UserGestureIndicator::ProcessingUserGesture()) {
172 autoplay_uma_helper_->OnAutoplayInitiated(AutoplaySource::kMethod);
173 if (IsGestureNeededForPlayback()) {
174 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
175 CrossOriginAutoplayResult::kAutoplayBlocked);
176 return kNotAllowedError;
177 }
178
179 if (IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()) {
180 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
181 CrossOriginAutoplayResult::kAutoplayBlocked);
182 } else {
183 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
184 CrossOriginAutoplayResult::kAutoplayAllowed);
185 }
186 } else {
187 autoplay_uma_helper_->RecordCrossOriginAutoplayResult(
188 CrossOriginAutoplayResult::kPlayedWithGesture);
189 TryUnlockingUserGesture();
190 }
191
192 return nullptr;
193 }
194
195 bool AutoplayPolicy::IsAutoplayingMuted() const {
196 return IsAutoplayingMutedInternal(element_->muted());
197 }
198
199 bool AutoplayPolicy::IsAutoplayingMutedInternal(bool muted) const {
200 if (!element_->IsHTMLVideoElement() ||
201 !RuntimeEnabledFeatures::autoplayMutedVideosEnabled()) {
202 return false;
203 }
204
205 return !element_->paused() && muted && IsLockedPendingUserGesture();
206 }
207
208 bool AutoplayPolicy::IsLockedPendingUserGesture() const {
209 return locked_pending_user_gesture_;
210 }
211
212 void AutoplayPolicy::TryUnlockingUserGesture() {
213 if (IsLockedPendingUserGesture() &&
214 UserGestureIndicator::UtilizeUserGesture()) {
215 UnlockUserGesture();
216 }
217 }
218
219 void AutoplayPolicy::UnlockUserGesture() {
220 locked_pending_user_gesture_ = false;
221 locked_pending_user_gesture_if_cross_origin_experiment_enabled_ = false;
222 }
223
224 bool AutoplayPolicy::IsGestureNeededForPlayback() const {
225 if (!locked_pending_user_gesture_)
226 return false;
227
228 return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked();
229 }
230
231 bool AutoplayPolicy::IsGestureNeededForPlaybackIfPendingUserGestureIsLocked()
232 const {
233 if (element_->GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream)
234 return false;
235
236 // We want to allow muted video to autoplay if:
237 // - the flag is enabled;
238 // - Data Saver is not enabled;
239 // - Preload was not disabled (low end devices);
240 // - Autoplay is enabled in settings;
241 if (element_->IsHTMLVideoElement() && element_->muted() &&
242 RuntimeEnabledFeatures::autoplayMutedVideosEnabled() &&
243 !(element_->GetDocument().GetSettings() &&
244 element_->GetDocument().GetSettings()->GetDataSaverEnabled()) &&
245 !(element_->GetDocument().GetSettings() &&
246 element_->GetDocument()
247 .GetSettings()
248 ->GetForcePreloadNoneForMediaElements()) &&
249 IsAutoplayAllowedPerSettings()) {
250 return false;
251 }
252
253 return true;
254 }
255
256 void AutoplayPolicy::OnVisibilityChangedForAutoplay(bool is_visible) {
257 if (!is_visible) {
258 if (element_->can_autoplay_ && element_->Autoplay()) {
259 element_->PauseInternal();
260 element_->can_autoplay_ = true;
261 }
262 return;
263 }
264
265 if (ShouldAutoplay()) {
266 element_->paused_ = false;
267 element_->ScheduleEvent(EventTypeNames::play);
268 element_->ScheduleNotifyPlaying();
269
270 element_->UpdatePlayState();
271 }
272 }
273
274 bool AutoplayPolicy::IsGestureNeededForPlaybackIfCrossOriginExperimentEnabled()
275 const {
276 if (!locked_pending_user_gesture_if_cross_origin_experiment_enabled_)
277 return false;
278
279 return IsGestureNeededForPlaybackIfPendingUserGestureIsLocked();
280 }
281
282 bool AutoplayPolicy::IsAutoplayAllowedPerSettings() const {
283 LocalFrame* frame = element_->GetDocument().GetFrame();
284 if (!frame)
285 return false;
286 return frame->GetContentSettingsClient()->AllowAutoplay(true);
287 }
288
289 bool AutoplayPolicy::ShouldAutoplay() {
290 if (element_->GetDocument().IsSandboxed(kSandboxAutomaticFeatures))
291 return false;
292 return element_->can_autoplay_ && element_->paused_ && element_->Autoplay();
293 }
294
295 DEFINE_TRACE(AutoplayPolicy) {
296 visitor->Trace(element_);
297 visitor->Trace(autoplay_visibility_observer_);
298 visitor->Trace(autoplay_uma_helper_);
299 }
300
301 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698