Diff
Modified: trunk/LayoutTests/ChangeLog (245466 => 245467)
--- trunk/LayoutTests/ChangeLog 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/LayoutTests/ChangeLog 2019-05-17 16:39:09 UTC (rev 245467)
@@ -1,3 +1,16 @@
+2019-05-17 Eric Carlson <[email protected]>
+
+ Allow sequential playback of media files when initial playback started with a user gesture
+ https://bugs.webkit.org/show_bug.cgi?id=197959
+ <rdar://problem/50655207>
+
+ Reviewed by Youenn Fablet.
+
+ * media/media-fullscreen.js: Insert a pause between tests to clear the user gesture
+ used in the first test.
+ * media/playlist-inherits-user-gesture-expected.txt: Added.
+ * media/playlist-inherits-user-gesture.html: Added.
+
2019-05-17 Truitt Savell <[email protected]>
Unmark several skipped tests in wk2
Modified: trunk/LayoutTests/media/media-fullscreen.js (245466 => 245467)
--- trunk/LayoutTests/media/media-fullscreen.js 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/LayoutTests/media/media-fullscreen.js 2019-05-17 16:39:09 UTC (rev 245467)
@@ -14,7 +14,10 @@
else {
if (movie.type == 'video')
testDOMException("mediaElement.webkitEnterFullScreen()", "DOMException.INVALID_STATE_ERR");
- openNextMovie();
+
+ // A user gesture will transfer across setTimeout for 1 second, so pause to let that
+ // expire before opening the next movie.
+ setTimeout(openNextMovie, 1010);
}
}
@@ -62,7 +65,6 @@
var movie = movieInfo.movies[movieInfo.current];
consoleWrite("* event handler NOT triggered by a user gesture");
-
if (movie.type == 'video') {
testExpected("mediaElement.webkitSupportsFullscreen", movie.supportsFS);
if (mediaElement.webkitSupportsPresentationMode)
@@ -80,10 +82,7 @@
testDOMException("mediaElement.webkitEnterFullScreen()", "DOMException.INVALID_STATE_ERR");
// Click on the button
- if (window.testRunner)
- setTimeout(clickEnterFullscreenButton, 10);
- else
- openNextMovie();
+ runWithKeyDown(clickEnterFullscreenButton);
}
function openNextMovie()
Added: trunk/LayoutTests/media/playlist-inherits-user-gesture-expected.txt (0 => 245467)
--- trunk/LayoutTests/media/playlist-inherits-user-gesture-expected.txt (rev 0)
+++ trunk/LayoutTests/media/playlist-inherits-user-gesture-expected.txt 2019-05-17 16:39:09 UTC (rev 245467)
@@ -0,0 +1,27 @@
+** Start first video with user gesture.
+RUN(window.internals.settings.setVideoPlaybackRequiresUserGesture(true);)
+RUN(video1 = document.createElement("video"))
+RUN(video1.src = "" "content/test"))
+RUN(document.body.appendChild(video1))
+EXPECTED (window.internals.pageMediaState().includes('HasUserInteractedWithMediaElement') == 'false') OK
+RUN(video1.play())
+EXPECTED (window.internals.pageMediaState().includes('HasUserInteractedWithMediaElement') == 'true') OK
+EVENT(playing)
+RUN(video1.currentTime = video1.duration - 0.2)
+EVENT(ended)
+
+** Start second video without user gesture but within inheritance window, should succeed.
+RUN(video2 = document.createElement("video"))
+RUN(video2.src = "" "content/test"))
+RUN(document.body.appendChild(video2))
+Promise resolved OK
+RUN(video2.currentTime = video2.duration - 0.2)
+EVENT(ended)
+
+** Start third video without user gesture but after inheritance window, should fail.
+RUN(video3 = document.createElement("video"))
+RUN(video3.src = "" "content/test"))
+RUN(document.body.appendChild(video3))
+Promise rejected correctly OK
+END OF TEST
+
Added: trunk/LayoutTests/media/playlist-inherits-user-gesture.html (0 => 245467)
--- trunk/LayoutTests/media/playlist-inherits-user-gesture.html (rev 0)
+++ trunk/LayoutTests/media/playlist-inherits-user-gesture.html 2019-05-17 16:39:09 UTC (rev 245467)
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>playlist-inherits-user-gesture</title>
+ <script src=""
+ <script src=""
+ <script>
+ async function runTest() {
+ consoleWrite("** Start first video with user gesture.")
+ if (window.internals)
+ run('window.internals.settings.setVideoPlaybackRequiresUserGesture(true);');
+ run('video1 = document.createElement("video")');
+ run('video1.src = "" "content/test")');
+ video1.controls = 1;
+ run('document.body.appendChild(video1)');
+
+ if (window.internals)
+ testExpected("window.internals.pageMediaState().includes('HasUserInteractedWithMediaElement')", false);
+ runWithKeyDown(() => {
+ run('video1.play()');
+ });
+ if (window.internals)
+ testExpected("window.internals.pageMediaState().includes('HasUserInteractedWithMediaElement')", true)
+
+ await waitFor(video1, 'playing');
+ run('video1.currentTime = video1.duration - 0.2');
+ await waitFor(video1, 'ended');
+
+ consoleWrite("<br>** Start second video without user gesture but within inheritance window, should succeed.")
+ run('video2 = document.createElement("video")');
+ run('video2.src = "" "content/test")');
+ video2.controls = 1;
+ run('document.body.appendChild(video2)');
+
+ await shouldResolve(video2.play());
+ run('video2.currentTime = video2.duration - 0.2');
+ await waitFor(video2, 'ended');
+
+ consoleWrite("<br>** Start third video without user gesture but after inheritance window, should fail.")
+ await sleepFor(1200);
+ run('video3 = document.createElement("video")');
+ run('video3.src = "" "content/test")');
+ video3.controls = 1;
+ run('document.body.appendChild(video3)');
+
+ await shouldReject(video3.play());
+
+ endTest();
+ }
+ </script>
+</head>
+<body _onload_="runTest()">
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (245466 => 245467)
--- trunk/Source/WebCore/ChangeLog 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/ChangeLog 2019-05-17 16:39:09 UTC (rev 245467)
@@ -1,3 +1,48 @@
+2019-05-17 Eric Carlson <[email protected]>
+
+ Allow sequential playback of media files when initial playback started with a user gesture
+ https://bugs.webkit.org/show_bug.cgi?id=197959
+ <rdar://problem/50655207>
+
+ Reviewed by Youenn Fablet.
+
+ Test: media/playlist-inherits-user-gesture.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::processingUserGestureForMedia const): Return true if it is within
+ one second of the last HTMLMediaElement 'ended' event.
+ * dom/Document.h:
+ (WebCore::Document::mediaFinishedPlaying):
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::parseAttribute): removeBehaviorsRestrictionsAfterFirstUserGesture ->
+ removeBehaviorRestrictionsAfterFirstUserGesture.
+ (WebCore::HTMLMediaElement::load): Ditto. Don't call removeBehaviorsRestrictionsAfterFirstUserGesture,
+ it will be done in prepareForLoad.
+ (WebCore::HTMLMediaElement::prepareForLoad): removeBehaviorsRestrictionsAfterFirstUserGesture ->
+ removeBehaviorRestrictionsAfterFirstUserGesture.
+ (WebCore::HTMLMediaElement::audioTrackEnabledChanged): Ditto.
+ (WebCore::HTMLMediaElement::play): Ditto.
+ (WebCore::HTMLMediaElement::pause): Ditto.
+ (WebCore::HTMLMediaElement::setVolume): Ditto.
+ (WebCore::HTMLMediaElement::setMuted): Ditto.
+ (WebCore::HTMLMediaElement::webkitShowPlaybackTargetPicker): Ditto.
+ (WebCore::HTMLMediaElement::dispatchEvent): Call document().mediaFinishedPlaying()
+ when dispatching the 'ended' event.
+ (WebCore::HTMLMediaElement::removeBehaviorRestrictionsAfterFirstUserGesture): Rename. Set
+ m_removedBehaviorRestrictionsAfterFirstUserGesture.
+ (WebCore::HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture): Deleted.
+ * html/HTMLMediaElement.h:
+
+ * html/HTMLVideoElement.cpp:
+ (WebCore:HTMLVideoElement::nativeImageForCurrentTime): Convert to runtime logging.
+ (WebCore:HTMLVideoElement::webkitEnterFullscreen): Ditto.
+ (WebCore:HTMLVideoElement::webkitSetPresentationMode): Ditto.
+ (WebCore:HTMLVideoElement::fullscreenModeChanged): Ditto.
+
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::removeBehaviorRestriction): Update log message.
+
2019-05-17 Brent Fulgham <[email protected]>
Hardening: Prevent FrameLoader crash due to SetForScope
Modified: trunk/Source/WebCore/dom/Document.cpp (245466 => 245467)
--- trunk/Source/WebCore/dom/Document.cpp 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/dom/Document.cpp 2019-05-17 16:39:09 UTC (rev 245467)
@@ -328,6 +328,7 @@
static const unsigned cMaxWriteRecursionDepth = 21;
bool Document::hasEverCreatedAnAXObjectCache = false;
+static const Seconds maxIntervalForUserGestureForwardingAfterMediaFinishesPlaying { 1_s };
// DOM Level 2 says (letters added):
//
@@ -6576,6 +6577,9 @@
if (UserGestureIndicator::processingUserGestureForMedia())
return true;
+ if (m_userActivatedMediaFinishedPlayingTimestamp + maxIntervalForUserGestureForwardingAfterMediaFinishesPlaying >= MonotonicTime::now())
+ return true;
+
if (settings().mediaUserGestureInheritsFromDocument())
return topDocument().hasHadUserInteraction();
Modified: trunk/Source/WebCore/dom/Document.h (245466 => 245467)
--- trunk/Source/WebCore/dom/Document.h 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/dom/Document.h 2019-05-17 16:39:09 UTC (rev 245467)
@@ -1221,6 +1221,7 @@
bool hasHadUserInteraction() const { return static_cast<bool>(m_lastHandledUserGestureTimestamp); }
void updateLastHandledUserGestureTimestamp(MonotonicTime);
bool processingUserGestureForMedia() const;
+ void userActivatedMediaFinishedPlaying() { m_userActivatedMediaFinishedPlayingTimestamp = MonotonicTime::now(); }
void setUserDidInteractWithPage(bool userDidInteractWithPage) { ASSERT(&topDocument() == this); m_userDidInteractWithPage = userDidInteractWithPage; }
bool userDidInteractWithPage() const { ASSERT(&topDocument() == this); return m_userDidInteractWithPage; }
@@ -1835,6 +1836,7 @@
std::unique_ptr<EventTargetSet> m_wheelEventTargets;
MonotonicTime m_lastHandledUserGestureTimestamp;
+ MonotonicTime m_userActivatedMediaFinishedPlayingTimestamp;
void clearScriptedAnimationController();
RefPtr<ScriptedAnimationController> m_scriptedAnimationController;
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (245466 => 245467)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2019-05-17 16:39:09 UTC (rev 245467)
@@ -839,7 +839,7 @@
setMediaGroup(value);
else if (name == autoplayAttr) {
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture();
+ removeBehaviorRestrictionsAfterFirstUserGesture();
} else if (name == titleAttr) {
if (m_mediaSession)
m_mediaSession->clientCharacteristicsChanged();
@@ -1178,9 +1178,6 @@
INFO_LOG(LOGIDENTIFIER);
- if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture();
-
prepareForLoad();
m_resourceSelectionTaskQueue.enqueueTask([this] {
prepareToPlay();
@@ -1193,8 +1190,11 @@
// The Media Element Load Algorithm
// 12 February 2017
- INFO_LOG(LOGIDENTIFIER);
+ ALWAYS_LOG(LOGIDENTIFIER, "gesture = ", processingUserGestureForMedia());
+ if (processingUserGestureForMedia())
+ removeBehaviorRestrictionsAfterFirstUserGesture();
+
// 1 - Abort any already-running instance of the resource selection algorithm for this element.
// Perform the cleanup required for the resource load algorithm to run.
stopPeriodicTimers();
@@ -1951,7 +1951,7 @@
if (m_audioTracks && m_audioTracks->contains(track))
m_audioTracks->scheduleChangeEvent();
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
+ removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
}
void HTMLMediaElement::textTrackModeChanged(TextTrack& track)
@@ -3515,7 +3515,7 @@
}
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture();
+ removeBehaviorRestrictionsAfterFirstUserGesture();
m_pendingPlayPromises.append(WTFMove(promise));
playInternal();
@@ -3532,7 +3532,7 @@
return;
}
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture();
+ removeBehaviorRestrictionsAfterFirstUserGesture();
playInternal();
}
@@ -3633,7 +3633,7 @@
return;
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::RequireUserGestureToControlControlsManager);
+ removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::RequireUserGestureToControlControlsManager);
pauseInternal();
}
@@ -3744,7 +3744,7 @@
#if !PLATFORM(IOS_FAMILY)
if (volume && processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
+ removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
m_volume = volume;
m_volumeInitialized = true;
@@ -3783,7 +3783,7 @@
bool mutedStateChanged = m_muted != muted;
if (mutedStateChanged || !m_explicitlyMuted) {
if (processingUserGestureForMedia()) {
- removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
+ removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
if (hasAudio() && muted)
userDidInterfereWithAutoplay();
@@ -5878,7 +5878,7 @@
{
ALWAYS_LOG(LOGIDENTIFIER);
if (processingUserGestureForMedia())
- removeBehaviorsRestrictionsAfterFirstUserGesture();
+ removeBehaviorRestrictionsAfterFirstUserGesture();
m_mediaSession->showPlaybackTargetPicker();
}
@@ -5917,6 +5917,9 @@
{
DEBUG_LOG(LOGIDENTIFIER, event.type());
+ if (m_removedBehaviorRestrictionsAfterFirstUserGesture && event.type() == eventNames().endedEvent)
+ document().userActivatedMediaFinishedPlaying();
+
HTMLElement::dispatchEvent(event);
}
@@ -7207,7 +7210,7 @@
}
#endif
-void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::BehaviorRestrictions mask)
+void HTMLMediaElement::removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::BehaviorRestrictions mask)
{
MediaElementSession::BehaviorRestrictions restrictionsToRemove = mask &
(MediaElementSession::RequireUserGestureForLoad
@@ -7222,6 +7225,8 @@
| MediaElementSession::InvisibleAutoplayNotPermitted
| MediaElementSession::RequireUserGestureToControlControlsManager);
+ m_removedBehaviorRestrictionsAfterFirstUserGesture = true;
+
m_mediaSession->removeBehaviorRestriction(restrictionsToRemove);
document().topDocument().noteUserInteractionWithMediaElement();
}
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (245466 => 245467)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2019-05-17 16:39:09 UTC (rev 245467)
@@ -562,6 +562,7 @@
#if !RELEASE_LOG_DISABLED
const Logger& logger() const final { return *m_logger.get(); }
const void* logIdentifier() const final { return m_logIdentifier; }
+ const char* logClassName() const final { return "HTMLMediaElement"; }
WTFLogChannel& logChannel() const final;
#endif
@@ -851,7 +852,7 @@
void changeNetworkStateFromLoadingToIdle();
- void removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::BehaviorRestrictions mask = MediaElementSession::AllRestrictions);
+ void removeBehaviorRestrictionsAfterFirstUserGesture(MediaElementSession::BehaviorRestrictions mask = MediaElementSession::AllRestrictions);
void updateMediaController();
bool isBlocked() const;
@@ -942,8 +943,6 @@
void setInActiveDocument(bool);
#if !RELEASE_LOG_DISABLED
- const char* logClassName() const final { return "HTMLMediaElement"; }
-
const void* mediaPlayerLogIdentifier() final { return logIdentifier(); }
const Logger& mediaPlayerLogger() final { return logger(); }
#endif
@@ -1201,6 +1200,7 @@
bool m_isPlayingToWirelessTarget { false };
bool m_playingOnSecondScreen { false };
+ bool m_removedBehaviorRestrictionsAfterFirstUserGesture { false };
};
String convertEnumerationToString(HTMLMediaElement::AutoplayEventPlaybackState);
Modified: trunk/Source/WebCore/html/HTMLVideoElement.cpp (245466 => 245467)
--- trunk/Source/WebCore/html/HTMLVideoElement.cpp 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/html/HTMLVideoElement.cpp 2019-05-17 16:39:09 UTC (rev 245467)
@@ -318,7 +318,7 @@
ExceptionOr<void> HTMLVideoElement::webkitEnterFullscreen()
{
- LOG(Media, "HTMLVideoElement::webkitEnterFullscreen(%p)", this);
+ ALWAYS_LOG(LOGIDENTIFIER);
if (isFullscreen())
return { };
@@ -333,7 +333,7 @@
void HTMLVideoElement::webkitExitFullscreen()
{
- LOG(Media, "HTMLVideoElement::webkitExitFullscreen(%p)", this);
+ ALWAYS_LOG(LOGIDENTIFIER);
if (isFullscreen())
exitFullscreen();
}
@@ -443,7 +443,7 @@
void HTMLVideoElement::webkitSetPresentationMode(VideoPresentationMode mode)
{
- LOG(Media, "HTMLVideoElement::webkitSetPresentationMode(%p) - %d", this, mode);
+ ALWAYS_LOG(LOGIDENTIFIER, mode);
setFullscreenMode(toFullscreenMode(mode));
}
@@ -483,7 +483,7 @@
void HTMLVideoElement::fullscreenModeChanged(VideoFullscreenMode mode)
{
if (mode != fullscreenMode()) {
- LOG(Media, "HTMLVideoElement::fullscreenModeChanged(%p) - mode changed from %i to %i", this, fullscreenMode(), mode);
+ ALWAYS_LOG(LOGIDENTIFIER, "changed from ", fullscreenMode(), ", to ", mode);
scheduleEvent(eventNames().webkitpresentationmodechangedEvent);
}
Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (245466 => 245467)
--- trunk/Source/WebCore/html/MediaElementSession.cpp 2019-05-17 16:27:34 UTC (rev 245466)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp 2019-05-17 16:39:09 UTC (rev 245467)
@@ -242,7 +242,7 @@
if (!(m_restrictions & restriction))
return;
- INFO_LOG(LOGIDENTIFIER, "removing ", restrictionNames(m_restrictions & restriction));
+ INFO_LOG(LOGIDENTIFIER, "removed ", restrictionNames(m_restrictions & restriction));
m_restrictions &= ~restriction;
}