https://bugs.webkit.org/show_bug.cgi?id=168985
<rdar://problem/
30739051>
Reviewed by Jer Noble.
Source/WebCore:
Disable autoplay of silent videos in low power mode on iOS to save battery.
We force the display of the start button when denying autoplay in low power
mode to allow the user to start playback.
Test: media/modern-media-controls/start-support/start-support-lowPowerMode.html
* Modules/mediacontrols/MediaControlsHost.cpp:
(WebCore::MediaControlsHost::shouldForceControlsDisplay):
* Modules/mediacontrols/MediaControlsHost.h:
* Modules/mediacontrols/MediaControlsHost.idl:
Add shouldForceControlsDisplay property on MediaControlsHost. This property
is set to true when we want to force the display for media controls. Currently,
this only returns true for autoplay videos, while in low power mode.
* Modules/modern-media-controls/media/controls-visibility-support.js:
(ControlsVisibilitySupport.prototype._updateControls):
Take into consideration MediaControlsHost.shouldForceControlsDisplay when
initializing shouldShowControls variable.
* Modules/modern-media-controls/media/start-support.js:
(StartSupport.prototype._shouldShowStartButton):
Show the start button when MediaControlsHost.shouldForceControlsDisplay
returns true.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
Add MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode restriction
to the session when low power mode is enabled so we know we need to force a
gesture to start playback of autoplay videos.
(WebCore::HTMLMediaElement::shouldForceControlsDisplay):
Add convenience function to decide if we should force display of media controls.
This returns true if the media element is a video with autoplay attribute and
its session has the MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode
restriction (i.e. we are in low power mode).
(WebCore::HTMLMediaElement::configureMediaControls):
Force requireControls variable to true if shouldForceControlsDisplay() returns
true. We do this here instead of inside HTMLMediaElement::controls() because
we do not want to change the value of media.controls exposed to JavaScript.
(WebCore::HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture):
Add MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode to the list
of restrictions that get removed on user gesture.
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::playbackPermitted):
Deny playback for videos that have the RequireUserGestureForVideoDueToLowPowerMode
restriction unless there is a user gesture.
* html/MediaElementSession.h:
Add new MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode
restriction.
LayoutTests:
Add layout test coverage.
* media/modern-media-controls/start-support/start-support-lowPowerMode-expected.txt: Added.
* media/modern-media-controls/start-support/start-support-lowPowerMode.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@213460
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-03-06 Chris Dumez <cdumez@apple.com>
+
+ [iOS] Disable autoplay of silent videos in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168985
+ <rdar://problem/30739051>
+
+ Reviewed by Jer Noble.
+
+ Add layout test coverage.
+
+ * media/modern-media-controls/start-support/start-support-lowPowerMode-expected.txt: Added.
+ * media/modern-media-controls/start-support/start-support-lowPowerMode.html: Added.
+
2017-03-06 Ryan Haddad <ryanhaddad@apple.com>
Mark media/modern-media-controls/icon-button/icon-button-active-state.html as flaky.
--- /dev/null
+Test that silent autoplay videos do not start playing without user gesture while in low power mode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Received 'canplaythrough' event
+PASS !!internals.shadowRoot(media).querySelector('button.start') became true
+PASS media.controls is false
+Pressing on the start button
+Received 'play' event
+PASS media.controls is false
+PASS internals.shadowRoot(media).querySelector('button.start') became null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+internals.setLowPowerModeEnabled(true);
+</script>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../resources/media-controls-loader.js" type="text/javascript"></script>
+<script src="../resources/media-controls-utils.js" type="text/javascript"></script>
+</head>
+<body>
+<video src="../../content/test.mp4" style="width: 320px; height: 240px;" autoplay></video>
+<script>
+description("Test that silent autoplay videos do not start playing without user gesture while in low power mode.");
+jsTestIsAsync = true;
+
+let hasUserGesture = false;
+const media = document.querySelector("video");
+
+function endTest()
+{
+ media.remove();
+ finishJSTest();
+}
+
+media.addEventListener("canplaythrough", function() {
+ debug("Received 'canplaythrough' event");
+ // We should display the start button since we denied autoplay and the user needs a way to start playback.
+ shouldBecomeEqual("!!internals.shadowRoot(media).querySelector('button.start')", "true", function() {
+ shouldBeFalse("media.controls");
+
+ debug("Pressing on the start button");
+ hasUserGesture = true;
+ pressOnElement(internals.shadowRoot(media).querySelector('button.start'));
+ });
+});
+
+media.addEventListener("play", function() {
+ debug("Received 'play' event");
+ shouldBeFalse("media.controls");
+ if (hasUserGesture) {
+ shouldBecomeEqual("internals.shadowRoot(media).querySelector('button.start')", "null", endTest);
+ } else {
+ testFailed("Media started playing without user interaction");
+ endTest();
+ }
+});
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
media/modern-media-controls/playback-support/playback-support-button-click.html [ Skip ]
media/modern-media-controls/fullscreen-support/ipad/fullscreen-support-tap.html [ Skip ]
media/modern-media-controls/start-support/start-support-click-to-start.html [ Skip ]
+media/modern-media-controls/start-support/start-support-lowPowerMode.html [ Skip ]
media/modern-media-controls/button/button.html [ Skip ]
# AirPlay cannot be tested on iOS
2017-03-06 Chris Dumez <cdumez@apple.com>
+ [iOS] Disable autoplay of silent videos in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168985
+ <rdar://problem/30739051>
+
+ Reviewed by Jer Noble.
+
+ Disable autoplay of silent videos in low power mode on iOS to save battery.
+ We force the display of the start button when denying autoplay in low power
+ mode to allow the user to start playback.
+
+ Test: media/modern-media-controls/start-support/start-support-lowPowerMode.html
+
+ * Modules/mediacontrols/MediaControlsHost.cpp:
+ (WebCore::MediaControlsHost::shouldForceControlsDisplay):
+ * Modules/mediacontrols/MediaControlsHost.h:
+ * Modules/mediacontrols/MediaControlsHost.idl:
+ Add shouldForceControlsDisplay property on MediaControlsHost. This property
+ is set to true when we want to force the display for media controls. Currently,
+ this only returns true for autoplay videos, while in low power mode.
+
+ * Modules/modern-media-controls/media/controls-visibility-support.js:
+ (ControlsVisibilitySupport.prototype._updateControls):
+ Take into consideration MediaControlsHost.shouldForceControlsDisplay when
+ initializing shouldShowControls variable.
+
+ * Modules/modern-media-controls/media/start-support.js:
+ (StartSupport.prototype._shouldShowStartButton):
+ Show the start button when MediaControlsHost.shouldForceControlsDisplay
+ returns true.
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::HTMLMediaElement):
+ Add MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode restriction
+ to the session when low power mode is enabled so we know we need to force a
+ gesture to start playback of autoplay videos.
+
+ (WebCore::HTMLMediaElement::shouldForceControlsDisplay):
+ Add convenience function to decide if we should force display of media controls.
+ This returns true if the media element is a video with autoplay attribute and
+ its session has the MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode
+ restriction (i.e. we are in low power mode).
+
+ (WebCore::HTMLMediaElement::configureMediaControls):
+ Force requireControls variable to true if shouldForceControlsDisplay() returns
+ true. We do this here instead of inside HTMLMediaElement::controls() because
+ we do not want to change the value of media.controls exposed to JavaScript.
+
+ (WebCore::HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture):
+ Add MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode to the list
+ of restrictions that get removed on user gesture.
+
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::playbackPermitted):
+ Deny playback for videos that have the RequireUserGestureForVideoDueToLowPowerMode
+ restriction unless there is a user gesture.
+
+ * html/MediaElementSession.h:
+ Add new MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode
+ restriction.
+
+2017-03-06 Chris Dumez <cdumez@apple.com>
+
LayoutTest fast/dom/timer-throttling-hidden-page.html is a flaky failure
https://bugs.webkit.org/show_bug.cgi?id=168927
return !m_mediaElement->mediaSession().playbackPermitted(*m_mediaElement);
}
+bool MediaControlsHost::shouldForceControlsDisplay() const
+{
+ return m_mediaElement->shouldForceControlsDisplay();
+}
+
String MediaControlsHost::externalDeviceDisplayName() const
{
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
bool isVideoLayerInline() const;
bool isInMediaDocument() const;
bool userGestureRequired() const;
+ bool shouldForceControlsDisplay() const;
void setPreparedToReturnVideoLayerToInline(bool);
void updateCaptionDisplaySizes();
readonly attribute boolean isVideoLayerInline;
readonly attribute boolean userGestureRequired;
readonly attribute boolean isInMediaDocument;
+ readonly attribute boolean shouldForceControlsDisplay;
readonly attribute DOMString externalDeviceDisplayName;
readonly attribute DeviceType externalDeviceType;
_updateControls()
{
const media = this.mediaController.media;
- const shouldShowControls = !!media.controls;
+ const host = this.mediaController.host;
+ const shouldShowControls = !!(media.controls || (host && host.shouldForceControlsDisplay));
const isVideo = media instanceof HTMLVideoElement && media.videoTracks.length > 0;
const controls = this.mediaController.controls;
_shouldShowStartButton()
{
const media = this.mediaController.media;
+ const host = this.mediaController.host;
+
+ if (host && host.shouldForceControlsDisplay)
+ return true;
if (this._hasPlayed || media.played.length)
return false;
if (media.error)
return false;
- const host = this.mediaController.host;
if (!media.controls && host && host.allowsInlineMediaPlayback)
return false;
m_sendProgressEvents = false;
#endif
+ auto* page = document.page();
+
if (document.settings().invisibleAutoplayNotPermitted())
m_mediaSession->addBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted);
m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForLoad);
}
+ if (page && page->isLowPowerModeEnabled())
+ m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode);
+
if (shouldAudioPlaybackRequireUserGesture)
m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForAudioRateChange);
#endif
#if ENABLE(VIDEO_TRACK)
- if (document.page())
- m_captionDisplayMode = document.page()->group().captionPreferences().captionDisplayMode();
+ if (page)
+ m_captionDisplayMode = page->group().captionPreferences().captionDisplayMode();
#endif
#if ENABLE(MEDIA_SESSION)
#endif
}
+bool HTMLMediaElement::shouldForceControlsDisplay() const
+{
+ // Always create controls for autoplay video that requires user gesture due to being in low power mode.
+ return isVideo() && autoplay() && m_mediaSession->hasBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode);
+}
+
void HTMLMediaElement::configureMediaControls()
{
bool requireControls = controls();
if (isVideo() && m_mediaSession->requiresFullscreenForVideoPlayback(*this))
requireControls = true;
+ if (shouldForceControlsDisplay())
+ requireControls = true;
+
// Always create controls when in full screen mode.
if (isFullscreen())
requireControls = true;
| MediaElementSession::RequireUserGestureForVideoRateChange
| MediaElementSession::RequireUserGestureForAudioRateChange
| MediaElementSession::RequireUserGestureForFullscreen
+ | MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode
| MediaElementSession::InvisibleAutoplayNotPermitted
| MediaElementSession::RequireUserGestureToControlControlsManager);
double percentLoaded() const;
+ bool shouldForceControlsDisplay() const;
+
#if ENABLE(VIDEO_TRACK)
ExceptionOr<TextTrack&> addTextTrack(const String& kind, const String& label, const String& language);
return MediaPlaybackDenialReason::UserGestureRequired;
}
+ if (m_restrictions & RequireUserGestureForVideoDueToLowPowerMode && element.isVideo() && !ScriptController::processingUserGestureForMedia()) {
+ LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of video low power mode restriction");
+ return MediaPlaybackDenialReason::UserGestureRequired;
+ }
+
return SuccessOr<MediaPlaybackDenialReason>();
}
OverrideUserGestureRequirementForMainContent = 1 << 12,
RequireUserGestureToControlControlsManager = 1 << 13,
RequirePlaybackToControlControlsManager = 1 << 14,
+ RequireUserGestureForVideoDueToLowPowerMode = 1 << 15,
AllRestrictions = ~NoRestrictions,
};
typedef unsigned BehaviorRestrictions;