Diff
Modified: trunk/LayoutTests/ChangeLog (275786 => 275787)
--- trunk/LayoutTests/ChangeLog 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/LayoutTests/ChangeLog 2021-04-10 00:45:37 UTC (rev 275787)
@@ -1,3 +1,18 @@
+2021-04-09 Jean-Yves Avenard <[email protected]>
+
+ Media Session action should default to the MediaElement's default when no MediaSession handler are set
+ https://bugs.webkit.org/show_bug.cgi?id=224278
+ <rdar://problem/76339841>
+
+ Reviewed by Youenn Fablet .
+
+ * media/audio-background-playback-playlist-expected.txt: Renamed method
+ * media/audio-background-playback-playlist.html: Renamed method
+ * media/media-session/default-actionHandlers-expected.txt: Added.
+ * media/media-session/default-actionHandlers.html: Added.
+ * platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt: Renamed method
+ * platform/mac/media/video-best-element-for-playback-controls-purpose.html: Renamed method
+
2021-04-09 Robert Jenner <[email protected]>
[ macOS ] webgl/1.0.3/conformance/glsl/constructors/glsl-construct-ivec2.html is a flakey timeout
Modified: trunk/LayoutTests/media/audio-background-playback-playlist-expected.txt (275786 => 275787)
--- trunk/LayoutTests/media/audio-background-playback-playlist-expected.txt 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/LayoutTests/media/audio-background-playback-playlist-expected.txt 2021-04-10 00:45:37 UTC (rev 275787)
@@ -4,18 +4,18 @@
EVENT(canplaythrough)
RUN(audio.play())
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying") == '[object HTMLAudioElement]') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("NowPlaying") == '[object HTMLAudioElement]') OK
RUN(internals.applicationDidEnterBackground(true))
RUN(audio.currentTime = audio.duration - 0.1)
EVENT(ended)
RUN(audio.src = ""
RUN(audio.load())
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying") == '[object HTMLAudioElement]') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("NowPlaying") == '[object HTMLAudioElement]') OK
RUN(audio.src = "" "content/test"))
RUN(audio.load())
EVENT(canplaythrough)
RUN(audio.play())
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying") == '[object HTMLAudioElement]') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("NowPlaying") == '[object HTMLAudioElement]') OK
END OF TEST
Modified: trunk/LayoutTests/media/audio-background-playback-playlist.html (275786 => 275787)
--- trunk/LayoutTests/media/audio-background-playback-playlist.html 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/LayoutTests/media/audio-background-playback-playlist.html 2021-04-10 00:45:37 UTC (rev 275787)
@@ -11,19 +11,19 @@
await waitFor(audio, 'canplaythrough');
runWithKeyDown('audio.play()');
await waitFor(audio, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying")', audio);
+ testExpected('internals.bestMediaElementForRemoteControls("NowPlaying")', audio);
run('internals.applicationDidEnterBackground(true)');
run('audio.currentTime = audio.duration - 0.1')
await waitFor(audio, 'ended');
run('audio.src = ""
run('audio.load()');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying")', audio);
+ testExpected('internals.bestMediaElementForRemoteControls("NowPlaying")', audio);
run('audio.src = "" "content/test")');
run('audio.load()');
await waitFor(audio, 'canplaythrough');
run('audio.play()');
await waitFor(audio, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("NowPlaying")', audio);
+ testExpected('internals.bestMediaElementForRemoteControls("NowPlaying")', audio);
endTest();
});
</script>
Added: trunk/LayoutTests/media/media-session/default-actionHandlers-expected.txt (0 => 275787)
--- trunk/LayoutTests/media/media-session/default-actionHandlers-expected.txt (rev 0)
+++ trunk/LayoutTests/media/media-session/default-actionHandlers-expected.txt 2021-04-10 00:45:37 UTC (rev 275787)
@@ -0,0 +1,29 @@
+
+RUN(video.src = "" "../content/test"))
+EVENT(loadeddata)
+Test that default media element action will be run when no media session handlers exist for that action.
+EXPECTED (video.paused == 'true') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"}))
+EXPECTED (video.paused == 'false') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"}))
+EXPECTED (video.paused == 'true') OK
+RUN(video.currentTime = 0)
+EXPECTED (video.currentTime == '0') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekto", seekTime: 1}))
+EXPECTED (video.currentTime == '1') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekforward", seekOffset: 5}))
+EXPECTED (video.currentTime == '6') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekbackward", seekOffset: 5}))
+EXPECTED (video.currentTime == '1') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"}))
+ACTION: play
+EXPECTED (video.paused == 'true') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"}))
+EXPECTED (video.paused == 'false') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"}))
+ACTION: pause
+EXPECTED (video.paused == 'false') OK
+RUN(internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"}))
+EXPECTED (video.paused == 'true') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/media-session/default-actionHandlers.html (0 => 275787)
--- trunk/LayoutTests/media/media-session/default-actionHandlers.html (rev 0)
+++ trunk/LayoutTests/media/media-session/default-actionHandlers.html 2021-04-10 00:45:37 UTC (rev 275787)
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>default-actionHandlers</title>
+ <script src=""
+ <script src=""
+ <script>
+
+ async function runTest() {
+ if (!window.internals) {
+ failTest('This test requires Internals');
+ return;
+ }
+
+ findMediaElement();
+ run('video.src = "" "../content/test")');
+ await waitFor(video, 'loadeddata');
+
+ consoleWrite('Test that default media element action will be run when no media session handlers exist for that action.');
+
+ testExpected("video.paused", true);
+
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"})');
+ testExpected("video.paused", false);
+
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"})');
+ testExpected("video.paused", true);
+
+ run('video.currentTime = 0');
+ testExpected("video.currentTime", 0);
+
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekto", seekTime: 1})');
+ testExpected("video.currentTime", 1);
+
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekforward", seekOffset: 5})');
+ testExpected("video.currentTime", 6);
+
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "seekbackward", seekOffset: 5})');
+ testExpected("video.currentTime", 1);
+
+ navigator.mediaSession.setActionHandler("play", actionDetails => {
+ consoleWrite(`ACTION: ${actionDetails.action}`);
+ });
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"})');
+ // Playback shouldn't have started if a handler for the play action was defined and it did nothing.
+ testExpected("video.paused", true);
+
+ navigator.mediaSession.setActionHandler("play", null);
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "play"})');
+ testExpected("video.paused", false);
+
+ navigator.mediaSession.setActionHandler("pause", actionDetails => {
+ consoleWrite(`ACTION: ${actionDetails.action}`);
+ });
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"})');
+ // Playback shouldn't have been interrupted if a handler for the pause action was defined and it did nothing.
+ testExpected("video.paused", false);
+
+ navigator.mediaSession.setActionHandler("pause", null);
+ run('internals.sendMediaSessionAction(navigator.mediaSession, {action: "pause"})');
+ testExpected("video.paused", true);
+
+ endTest();
+ }
+ </script>
+</head>
+<body _onload_="runTest()">
+ <video controls preload='auto'></video>
+</body>
+</html>
Modified: trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt (275786 => 275787)
--- trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt 2021-04-10 00:45:37 UTC (rev 275787)
@@ -1,31 +1,31 @@
Unloaded video elements should not be considered main content.
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
Large, autoplay videos with video and audio should be considered main content.
RUN(video = createVideo({autoplay: true, type: "audio+video", size: "large"}))
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == '[object HTMLVideoElement]') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == '[object HTMLVideoElement]') OK
Small, autoplay videos with video and audio should be considered main content.
RUN(video = createVideo({autoplay: true, type: "audio+video", size: "small"}))
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
Muted autoplay videos should not be considered main content.
RUN(video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"}))
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
Video-only autoplay videos should not be considered main content.
RUN(video = createVideo({autoplay: true, type: "video", size: "large"}))
EVENT(playing)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
Non-playing videos should not be considered main content.
RUN(video = createVideo({type: "audio+video", size: "large"}))
EVENT(canplaythrough)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
Large, autoplay videos outside fullscreen element should not be considered main content
RUN(video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"}))
@@ -32,7 +32,7 @@
EVENT(playing)
RUN(document.querySelector("#fullscreen").webkitRequestFullscreen())
EVENT(webkitfullscreenchange)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == 'null') OK
RUN(document.webkitExitFullscreen())
EVENT(webkitfullscreenchange)
@@ -41,7 +41,7 @@
EVENT(playing)
RUN(document.querySelector("#target").webkitRequestFullscreen())
EVENT(webkitfullscreenchange)
-EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == '[object HTMLVideoElement]') OK
+EXPECTED (internals.bestMediaElementForRemoteControls("ControlsManager") == '[object HTMLVideoElement]') OK
RUN(document.webkitExitFullscreen())
EVENT(webkitfullscreenchange)
Modified: trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html (275786 => 275787)
--- trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html 2021-04-10 00:45:37 UTC (rev 275787)
@@ -63,7 +63,7 @@
function() {
consoleWrite('Unloaded video elements should not be considered main content.');
video = createVideo();
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null)
},
async function() {
@@ -70,7 +70,7 @@
consoleWrite('Large, autoplay videos with video and audio should be considered main content.')
run('video = createVideo({autoplay: true, type: "audio+video", size: "large"})');
await waitFor(video, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', video)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', video)
},
async function() {
@@ -77,7 +77,7 @@
consoleWrite('Small, autoplay videos with video and audio should be considered main content.')
run('video = createVideo({autoplay: true, type: "audio+video", size: "small"})');
await waitFor(video, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null)
},
async function() {
@@ -84,7 +84,7 @@
consoleWrite('Muted autoplay videos should not be considered main content.')
run('video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"})');
await waitFor(video, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null)
},
async function() {
@@ -91,7 +91,7 @@
consoleWrite('Video-only autoplay videos should not be considered main content.')
run('video = createVideo({autoplay: true, type: "video", size: "large"})');
await waitFor(video, 'playing');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null)
},
async function() {
@@ -98,7 +98,7 @@
consoleWrite('Non-playing videos should not be considered main content.')
run('video = createVideo({type: "audio+video", size: "large"})');
await waitFor(video, 'canplaythrough');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null)
},
async function() {
@@ -106,7 +106,7 @@
run('video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"})');
await waitFor(video, 'playing');
await enterFullscreen('#fullscreen');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null);
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', null);
await exitFullscreen();
},
@@ -115,7 +115,7 @@
run('video = createVideo({autoplay: true, type: "audio+video", size: "large"})');
await waitFor(video, 'playing');
await enterFullscreen('#target');
- testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', video);
+ testExpected('internals.bestMediaElementForRemoteControls("ControlsManager")', video);
await exitFullscreen();
},
];
Modified: trunk/Source/WebCore/ChangeLog (275786 => 275787)
--- trunk/Source/WebCore/ChangeLog 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/ChangeLog 2021-04-10 00:45:37 UTC (rev 275787)
@@ -1,3 +1,56 @@
+2021-04-09 Jean-Yves Avenard <[email protected]>
+
+ Media Session action should default to the MediaElement's default when no MediaSession handler are set
+ https://bugs.webkit.org/show_bug.cgi?id=224278
+ <rdar://problem/76339841>
+
+ Reviewed by Youenn Fablet .
+
+ When a media session doesn't explicitly define an action handler, we use the media element
+ default action of the same type.
+ Media Session doesn't track a particular media element, instead it loosely defines a guessed playback state
+ that tracks if some element in the current document is playing and not muted or otherwise paused.
+ (see https://w3c.github.io/mediasession/#playback-state-model)
+ We therefore need to determine what is currently the most suitable media element available in this document.
+ Unlike the Media Controller and the Now Playing policy that will only ever select a media
+ if it is currently playing and not muted, for the Media Session we may have to interact with paused media elements.
+ For this we add a new PlaybackControlsPurpose named MediaSession defining new search criterias.
+ A media element will be up for selection if it's playable (according to autoplay policy). From then,
+ the best element will be selected accoring to its visibility, its size, if it's beeing played previously and
+ the last time it was interacted with.
+
+ Test: media/media-session/default-actionHandlers.html
+
+ * Modules/mediasession/MediaSession.cpp:
+ (WebCore::platformCommandForMediaSessionAction): New convenience method to convert one datatype into another.
+ (WebCore::MediaSession::callActionHandler): Use user defined handler if present or run the related action on
+ the most suitable media element
+ (WebCore::MediaSession::activeMediaElement const): Determine the currently active media element in the current
+ Media Session's document.
+ * Modules/mediasession/MediaSession.h:
+ * html/HTMLMediaElement.cpp:
+ (WebCore::mediaElementSessionInfoForSession): Add new hasEverNotifiedAboutPlaying member.
+ (WebCore::preferMediaControlsForCandidateSessionOverOtherCandidateSession): Amend sorting algorithm to cater
+ for the MediaSession criterias described above.
+ (WebCore::mediaSessionMayBeConfusedWithMainContent): When using MediaSession purpose, there is no possible
+ ambiguity, amend as such.
+ (WebCore::HTMLMediaElement::bestMediaElementForRemoteControls): Renamed from bestMediaElementForShowingPlaybackControlsManager
+ method to better match how the method is actually used.
+ * html/HTMLMediaElement.h:
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::canShowControlsManager const): Amend for new Media Session criterias.
+ (WebCore::MediaElementSession::didReceiveRemoteControlCommand): Always let MediaSession manage the actions handling.
+ * html/MediaElementSession.h:
+ * page/Page.cpp:
+ (WebCore::Page::playbackControlsManagerUpdateTimerFired): amend following method rename.
+ * platform/audio/cocoa/MediaSessionManagerCocoa.mm:
+ (WebCore::MediaSessionManagerCocoa::nowPlayingEligibleSession): amend following method rename.
+ * testing/Internals.cpp:
+ (WebCore::Internals::bestMediaElementForRemoteControls): Renamed from bestMediaElementForShowingPlaybackControlsManager as above.
+ (WebCore::Internals::sendMediaSessionAction): amend following method rename.
+ * testing/Internals.h: Rename method
+ * testing/Internals.idl: Rename method
+
2021-04-09 Fujii Hironori <[email protected]>
[Cairo][GPUP] GraphicsContextGLOpenGL::paintToCanvas can't paint into a remote canvas
Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp (275786 => 275787)
--- trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp 2021-04-10 00:45:37 UTC (rev 275787)
@@ -30,6 +30,7 @@
#include "DOMWindow.h"
#include "EventNames.h"
+#include "HTMLMediaElement.h"
#include "JSMediaPositionState.h"
#include "JSMediaSessionAction.h"
#include "JSMediaSessionPlaybackState.h"
@@ -76,6 +77,53 @@
return PlatformMediaSession::NoCommand;
}
+static Optional<std::pair<PlatformMediaSession::RemoteControlCommandType, PlatformMediaSession::RemoteCommandArgument>> platformCommandForMediaSessionAction(const MediaSessionActionDetails& actionDetails)
+{
+ PlatformMediaSession::RemoteControlCommandType command = PlatformMediaSession::NoCommand;
+ PlatformMediaSession::RemoteCommandArgument argument;
+
+ switch (actionDetails.action) {
+ case MediaSessionAction::Play:
+ command = PlatformMediaSession::PlayCommand;
+ break;
+ case MediaSessionAction::Pause:
+ command = PlatformMediaSession::PauseCommand;
+ break;
+ case MediaSessionAction::Seekbackward:
+ command = PlatformMediaSession::SkipBackwardCommand;
+ argument.time = actionDetails.seekOffset;
+ break;
+ case MediaSessionAction::Seekforward:
+ command = PlatformMediaSession::SkipForwardCommand;
+ argument.time = actionDetails.seekOffset;
+ break;
+ case MediaSessionAction::Previoustrack:
+ command = PlatformMediaSession::PreviousTrackCommand;
+ break;
+ case MediaSessionAction::Nexttrack:
+ command = PlatformMediaSession::NextTrackCommand;
+ break;
+ case MediaSessionAction::Skipad:
+ // Not supported at present.
+ break;
+ case MediaSessionAction::Stop:
+ command = PlatformMediaSession::StopCommand;
+ break;
+ case MediaSessionAction::Seekto:
+ command = PlatformMediaSession::SeekToPlaybackPositionCommand;
+ argument.time = actionDetails.seekTime;
+ argument.fastSeek = actionDetails.fastSeek;
+ break;
+ case MediaSessionAction::Settrack:
+ // Not supported at present.
+ break;
+ }
+ if (command == PlatformMediaSession::NoCommand)
+ return { };
+
+ return std::make_pair(command, argument);
+}
+
Ref<MediaSession> MediaSession::create(Navigator& navigator)
{
return adoptRef(*new MediaSession(navigator));
@@ -199,22 +247,23 @@
notifyActionHandlerObservers();
}
-bool MediaSession::hasActionHandler(MediaSessionAction action) const
+bool MediaSession::callActionHandler(const MediaSessionActionDetails& actionDetails)
{
- return m_actionHandlers.contains(action);
-}
+ if (auto handler = m_actionHandlers.get(actionDetails.action)) {
+ handler->handleEvent(actionDetails);
+ return true;
+ }
+ auto element = activeMediaElement();
+ if (!element)
+ return false;
-RefPtr<MediaSessionActionHandler> MediaSession::handlerForAction(MediaSessionAction action) const
-{
- return m_actionHandlers.get(action);
+ auto platformCommand = platformCommandForMediaSessionAction(actionDetails);
+ if (!platformCommand)
+ return false;
+ element->didReceiveRemoteControlCommand(platformCommand->first, platformCommand->second);
+ return true;
}
-void MediaSession::callActionHandler(const MediaSessionActionDetails& actionDetails)
-{
- if (auto handler = m_actionHandlers.get(actionDetails.action))
- handler->handleEvent(actionDetails);
-}
-
ExceptionOr<void> MediaSession::setPositionState(Optional<MediaPositionState>&& state)
{
if (state)
@@ -314,6 +363,15 @@
});
}
+RefPtr<HTMLMediaElement> MediaSession::activeMediaElement() const
+{
+ auto* doc = document();
+ if (!doc)
+ return nullptr;
+
+ return HTMLMediaElement::bestMediaElementForRemoteControls(MediaElementSession::PlaybackControlsPurpose::MediaSession, doc);
+}
+
#if ENABLE(MEDIA_SESSION_COORDINATOR)
void MediaSession::notifyReadyStateObservers()
{
Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.h (275786 => 275787)
--- trunk/Source/WebCore/Modules/mediasession/MediaSession.h 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.h 2021-04-10 00:45:37 UTC (rev 275787)
@@ -45,6 +45,7 @@
namespace WebCore {
class Document;
+class HTMLMediaElement;
class MediaMetadata;
class MediaSessionCoordinator;
class Navigator;
@@ -84,12 +85,9 @@
ExceptionOr<void> setPlaylist(ScriptExecutionContext&, Vector<RefPtr<MediaMetadata>>&&);
#endif
- bool hasActionHandler(MediaSessionAction) const;
- WEBCORE_EXPORT RefPtr<MediaSessionActionHandler> handlerForAction(MediaSessionAction) const;
bool hasActiveActionHandlers() const { return !m_actionHandlers.isEmpty(); }
+ WEBCORE_EXPORT bool callActionHandler(const MediaSessionActionDetails&);
- void callActionHandler(const MediaSessionActionDetails&);
-
const Logger& logger() const { return *m_logger.get(); }
// EventTarget
@@ -134,6 +132,8 @@
const char* activeDOMObjectName() const final { return "MediaSession"; }
bool virtualHasPendingActivity() const final;
+ RefPtr<HTMLMediaElement> activeMediaElement() const;
+
WeakPtr<Navigator> m_navigator;
RefPtr<MediaMetadata> m_metadata;
MediaSessionPlaybackState m_playbackState { MediaSessionPlaybackState::None };
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (275786 => 275787)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-04-10 00:45:37 UTC (rev 275787)
@@ -325,6 +325,7 @@
bool isVisibleInViewportOrFullscreen : 1;
bool isLargeEnoughForMainContent : 1;
bool isPlayingAudio : 1;
+ bool hasEverNotifiedAboutPlaying : 1;
};
static MediaElementSessionInfo mediaElementSessionInfoForSession(const MediaElementSession& session, MediaElementSession::PlaybackControlsPurpose purpose)
@@ -337,7 +338,8 @@
session.canShowControlsManager(purpose),
element.isFullscreen() || element.isVisibleInViewport(),
session.isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls),
- element.isPlaying() && element.hasAudio() && !element.muted()
+ element.isPlaying() && element.hasAudio() && !element.muted(),
+ element.hasEverNotifiedAboutPlaying()
};
}
@@ -346,14 +348,21 @@
MediaElementSession::PlaybackControlsPurpose purpose = session.purpose;
ASSERT(purpose == otherSession.purpose);
- // For the controls manager, prioritize visible media over offscreen media.
- if (purpose == MediaElementSession::PlaybackControlsPurpose::ControlsManager && session.isVisibleInViewportOrFullscreen != otherSession.isVisibleInViewportOrFullscreen)
+ // For the controls manager and MediaSession, prioritize visible media over offscreen media.
+ if ((purpose == MediaElementSession::PlaybackControlsPurpose::ControlsManager || purpose == MediaElementSession::PlaybackControlsPurpose::MediaSession)
+ && session.isVisibleInViewportOrFullscreen != otherSession.isVisibleInViewportOrFullscreen)
return session.isVisibleInViewportOrFullscreen;
- // For Now Playing, prioritize elements that would normally satisfy main content.
- if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying && session.isLargeEnoughForMainContent != otherSession.isLargeEnoughForMainContent)
+ // For Now Playing and MediaSession, prioritize elements that would normally satisfy main content.
+ if ((purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying || purpose == MediaElementSession::PlaybackControlsPurpose::MediaSession)
+ && session.isLargeEnoughForMainContent != otherSession.isLargeEnoughForMainContent)
return session.isLargeEnoughForMainContent;
+ // For MediaSession, prioritize elements that have been played before.
+ if (purpose == MediaElementSession::PlaybackControlsPurpose::MediaSession
+ && session.hasEverNotifiedAboutPlaying != otherSession.hasEverNotifiedAboutPlaying)
+ return session.hasEverNotifiedAboutPlaying;
+
// As a tiebreaker, prioritize elements that the user recently interacted with.
return session.timeOfLastUserInteraction > otherSession.timeOfLastUserInteraction;
}
@@ -360,6 +369,9 @@
static bool mediaSessionMayBeConfusedWithMainContent(const MediaElementSessionInfo& session, MediaElementSession::PlaybackControlsPurpose purpose)
{
+ if (purpose == MediaElementSession::PlaybackControlsPurpose::MediaSession)
+ return false;
+
if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying)
return session.isPlayingAudio;
@@ -581,12 +593,13 @@
ThreadableBlobRegistry::unregisterBlobURL(m_blobURLForReading);
}
-RefPtr<HTMLMediaElement> HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose purpose)
+RefPtr<HTMLMediaElement> HTMLMediaElement::bestMediaElementForRemoteControls(MediaElementSession::PlaybackControlsPurpose purpose, const Document* document)
{
Vector<MediaElementSessionInfo> candidateSessions;
bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
- PlatformMediaSessionManager::sharedManager().forEachMatchingSession([](auto& session) {
- return is<MediaElementSession>(session);
+ PlatformMediaSessionManager::sharedManager().forEachMatchingSession([document](auto& session) {
+ return is<MediaElementSession>(session)
+ && (!document || &downcast<MediaElementSession>(session).element().document() == document);
}, [&](auto& session) {
auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(session), purpose);
if (mediaElementSessionInfo.canShowControlsManager)
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (275786 => 275787)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2021-04-10 00:45:37 UTC (rev 275787)
@@ -46,6 +46,7 @@
#include "VisibilityChangeClient.h"
#include <wtf/Function.h>
#include <wtf/LoggerHelper.h>
+#include <wtf/Optional.h>
#include <wtf/WeakPtr.h>
#if USE(AUDIO_SESSION) && PLATFORM(MAC)
@@ -155,7 +156,7 @@
static HashSet<HTMLMediaElement*>& allMediaElements();
- WEBCORE_EXPORT static RefPtr<HTMLMediaElement> bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose);
+ WEBCORE_EXPORT static RefPtr<HTMLMediaElement> bestMediaElementForRemoteControls(MediaElementSession::PlaybackControlsPurpose, const Document* = nullptr);
WEBCORE_EXPORT void rewind(double timeDelta);
WEBCORE_EXPORT void returnToRealtime() override;
@@ -573,6 +574,8 @@
WEBCORE_EXPORT void setOverridePreferredDynamicRangeMode(DynamicRangeMode);
void setPreferredDynamicRangeMode(DynamicRangeMode);
+ void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType, const PlatformMediaSession::RemoteCommandArgument&) override;
+
protected:
HTMLMediaElement(const QualifiedName&, Document&, bool createdByParser);
virtual void finishInitialization();
@@ -866,7 +869,6 @@
void resumeAutoplaying() override;
void mayResumePlayback(bool shouldResume) override;
bool canReceiveRemoteControlCommands() const override { return true; }
- void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType, const PlatformMediaSession::RemoteCommandArgument&) override;
bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const override;
bool shouldOverrideBackgroundLoadingRestriction() const override;
bool canProduceAudio() const final;
Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (275786 => 275787)
--- trunk/Source/WebCore/html/MediaElementSession.cpp 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp 2021-04-10 00:45:37 UTC (rev 275787)
@@ -525,7 +525,7 @@
return true;
}
- if (client().presentationType() == MediaType::Audio && purpose == PlaybackControlsPurpose::ControlsManager) {
+ if (client().presentationType() == MediaType::Audio && (purpose == PlaybackControlsPurpose::ControlsManager || purpose == PlaybackControlsPurpose::MediaSession)) {
if (!hasBehaviorRestriction(RequireUserGestureToControlControlsManager) || m_element.document().processingUserGestureForMedia()) {
INFO_LOG(LOGIDENTIFIER, "returning TRUE: audio element with user gesture");
return true;
@@ -565,7 +565,7 @@
return false;
}
- if (!m_element.hasEverNotifiedAboutPlaying()) {
+ if (purpose != PlaybackControlsPurpose::MediaSession && !m_element.hasEverNotifiedAboutPlaying()) {
INFO_LOG(LOGIDENTIFIER, "returning FALSE: hasn't fired playing notification");
return false;
}
@@ -597,7 +597,7 @@
}
}
- if (purpose == PlaybackControlsPurpose::NowPlaying) {
+ if (purpose == PlaybackControlsPurpose::NowPlaying || purpose == PlaybackControlsPurpose::MediaSession) {
INFO_LOG(LOGIDENTIFIER, "returning TRUE: potentially plays audio");
return true;
}
@@ -1135,10 +1135,7 @@
return;
}
- if (auto handler = session->handlerForAction(actionDetails.action))
- handler->handleEvent(actionDetails);
- else
- ALWAYS_LOG(LOGIDENTIFIER, "Ignoring command, no action handler registered for ", actionDetails.action);
+ session->callActionHandler(actionDetails);
}
#endif
Modified: trunk/Source/WebCore/html/MediaElementSession.h (275786 => 275787)
--- trunk/Source/WebCore/html/MediaElementSession.h 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/html/MediaElementSession.h 2021-04-10 00:45:37 UTC (rev 275787)
@@ -158,7 +158,7 @@
bool wantsToObserveViewportVisibilityForMediaControls() const;
bool wantsToObserveViewportVisibilityForAutoplay() const;
- enum class PlaybackControlsPurpose { ControlsManager, NowPlaying };
+ enum class PlaybackControlsPurpose { ControlsManager, NowPlaying, MediaSession };
bool canShowControlsManager(PlaybackControlsPurpose) const;
bool isLargeEnoughForMainContent(MediaSessionMainContentPurpose) const;
bool isMainContentForPurposesOfAutoplayEvents() const;
Modified: trunk/Source/WebCore/page/Page.cpp (275786 => 275787)
--- trunk/Source/WebCore/page/Page.cpp 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/page/Page.cpp 2021-04-10 00:45:37 UTC (rev 275787)
@@ -2135,7 +2135,7 @@
void Page::playbackControlsManagerUpdateTimerFired()
{
- if (auto bestMediaElement = HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose::ControlsManager))
+ if (auto bestMediaElement = HTMLMediaElement::bestMediaElementForRemoteControls(MediaElementSession::PlaybackControlsPurpose::ControlsManager))
chrome().client().setUpPlaybackControlsManager(*bestMediaElement);
else
chrome().client().clearPlaybackControlsManager();
Modified: trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm (275786 => 275787)
--- trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-04-10 00:45:37 UTC (rev 275787)
@@ -339,7 +339,7 @@
PlatformMediaSession* MediaSessionManagerCocoa::nowPlayingEligibleSession()
{
// FIXME: Fix this layering violation.
- if (auto element = HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose::NowPlaying))
+ if (auto element = HTMLMediaElement::bestMediaElementForRemoteControls(MediaElementSession::PlaybackControlsPurpose::NowPlaying))
return &element->mediaSession();
return nullptr;
Modified: trunk/Source/WebCore/testing/Internals.cpp (275786 => 275787)
--- trunk/Source/WebCore/testing/Internals.cpp 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/testing/Internals.cpp 2021-04-10 00:45:37 UTC (rev 275787)
@@ -4423,9 +4423,9 @@
}
#if ENABLE(VIDEO)
-RefPtr<HTMLMediaElement> Internals::bestMediaElementForShowingPlaybackControlsManager(Internals::PlaybackControlsPurpose purpose)
+RefPtr<HTMLMediaElement> Internals::bestMediaElementForRemoteControls(Internals::PlaybackControlsPurpose purpose)
{
- return HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(purpose);
+ return HTMLMediaElement::bestMediaElementForRemoteControls(purpose);
}
Internals::MediaSessionState Internals::mediaSessionState(HTMLMediaElement& element)
@@ -6121,10 +6121,9 @@
ExceptionOr<void> Internals::sendMediaSessionAction(MediaSession& session, const MediaSessionActionDetails& actionDetails)
{
- if (auto handler = session.handlerForAction(actionDetails.action)) {
- handler->handleEvent(actionDetails);
+ if (session.callActionHandler(actionDetails))
return { };
- }
+
return Exception { InvalidStateError };
}
Modified: trunk/Source/WebCore/testing/Internals.h (275786 => 275787)
--- trunk/Source/WebCore/testing/Internals.h 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/testing/Internals.h 2021-04-10 00:45:37 UTC (rev 275787)
@@ -926,7 +926,7 @@
#if ENABLE(VIDEO)
using PlaybackControlsPurpose = MediaElementSession::PlaybackControlsPurpose;
- RefPtr<HTMLMediaElement> bestMediaElementForShowingPlaybackControlsManager(PlaybackControlsPurpose);
+ RefPtr<HTMLMediaElement> bestMediaElementForRemoteControls(PlaybackControlsPurpose);
using MediaSessionState = PlatformMediaSession::State;
MediaSessionState mediaSessionState(HTMLMediaElement&);
Modified: trunk/Source/WebCore/testing/Internals.idl (275786 => 275787)
--- trunk/Source/WebCore/testing/Internals.idl 2021-04-10 00:03:58 UTC (rev 275786)
+++ trunk/Source/WebCore/testing/Internals.idl 2021-04-10 00:45:37 UTC (rev 275787)
@@ -905,7 +905,7 @@
[Conditional=VIDEO] readonly attribute NowPlayingState nowPlayingState;
- [Conditional=VIDEO] HTMLMediaElement bestMediaElementForShowingPlaybackControlsManager(PlaybackControlsPurpose purpose);
+ [Conditional=VIDEO] HTMLMediaElement bestMediaElementForRemoteControls(PlaybackControlsPurpose purpose);
[Conditional=VIDEO] MediaSessionState mediaSessionState(HTMLMediaElement element);
[Conditional=VIDEO] MediaUsageState mediaUsageState(HTMLMediaElement element);