Diff
Modified: trunk/Source/WebCore/ChangeLog (272444 => 272445)
--- trunk/Source/WebCore/ChangeLog 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/ChangeLog 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,3 +1,87 @@
+2021-02-05 Eric Carlson <[email protected]>
+
+ [Mac] Connect MediaSession with MediaRemote and NowPlaying
+ https://bugs.webkit.org/show_bug.cgi?id=221431
+ <rdar://problem/74000363>
+
+ Reviewed by Jer Noble.
+
+ Route all remote control commands to navigator.mediaSession when it has any
+ registered action handlers, and pass navigator.mediaSession.metadata to NowPlaying.
+
+ Tested with new API tests in Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaSession.mm.
+
+ * Modules/mediasession/MediaImage.h:
+ (WebCore::MediaImage::encode const):
+ (WebCore::MediaImage::decode):
+ * Modules/mediasession/MediaMetadata.cpp:
+ (WebCore::MediaMetadata::setTitle):
+ (WebCore::MediaMetadata::setArtist):
+ (WebCore::MediaMetadata::setAlbum):
+ (WebCore::MediaMetadata::setArtwork):
+ * Modules/mediasession/MediaMetadata.h:
+ (WebCore::MediaMetadata::title const):
+ (WebCore::MediaMetadata::artist const):
+ (WebCore::MediaMetadata::album const):
+ (WebCore::MediaMetadata::artwork const):
+ (WebCore::MediaMetadata::metadata const):
+ * Modules/mediasession/MediaMetadataInit.h:
+ (WebCore::MediaMetadataInit::encode const):
+ (WebCore::MediaMetadataInit::decode):
+ * Modules/mediasession/MediaSession.cpp:
+ (WebCore::nextLogIdentifier):
+ (WebCore::logChannel):
+ (WebCore::logClassName):
+ (WebCore::platformCommandForMediaSessionAction):
+ (WebCore::MediaSession::MediaSession):
+ (WebCore::MediaSession::setMetadata):
+ (WebCore::MediaSession::setPlaybackState):
+ (WebCore::MediaSession::setActionHandler):
+ (WebCore::MediaSession::setPositionState):
+ * Modules/mediasession/MediaSession.h:
+ (WebCore::MediaSession::hasActiveActionHandlers const):
+ (WebCore::MediaSession::logger const):
+ (WebCore::MediaSession::logIdentifier const):
+ (WTF::LogArgument<WebCore::MediaSessionPlaybackState>::toString):
+ (WTF::LogArgument<WebCore::MediaSessionAction>::toString):
+ * WebCore.xcodeproj/project.pbxproj:
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::didReceiveRemoteControlCommand):
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::didReceiveRemoteControlCommand):
+ (WebCore::MediaElementSession::nowPlayingInfo const):
+ * html/MediaElementSession.h:
+ * page/ChromeClient.h:
+ * platform/RemoteCommandListener.cpp:
+ (WebCore::RemoteCommandListener::scheduleSupportedCommandsUpdate):
+ (WebCore::RemoteCommandListener::addSupportedCommand):
+ (WebCore::RemoteCommandListener::removeSupportedCommand):
+ * platform/RemoteCommandListener.h:
+ * platform/audio/NowPlayingInfo.h:
+ (WebCore::NowPlayingInfo::decode):
+ * platform/audio/PlatformMediaSession.cpp:
+ (WebCore::convertEnumerationToString):
+ * platform/audio/PlatformMediaSession.h:
+ * platform/audio/PlatformMediaSessionManager.h:
+ (WebCore::PlatformMediaSessionManager::addSupportedCommand):
+ (WebCore::PlatformMediaSessionManager::removeSupportedCommand):
+ * platform/audio/cocoa/MediaSessionManagerCocoa.h:
+ * platform/audio/cocoa/MediaSessionManagerCocoa.mm:
+ (WebCore::MediaSessionManagerCocoa::scheduleSessionStatusUpdate):
+ (WebCore::MediaSessionManagerCocoa::sessionCanProduceAudioChanged):
+ (WebCore::MediaSessionManagerCocoa::addSupportedCommand):
+ (WebCore::MediaSessionManagerCocoa::removeSupportedCommand):
+ (WebCore::MediaSessionManagerCocoa::setNowPlayingInfo):
+ * platform/mac/MediaRemoteSoftLink.cpp:
+ * platform/mac/MediaRemoteSoftLink.h:
+ * platform/mac/RemoteCommandListenerMac.h:
+ * platform/mac/RemoteCommandListenerMac.mm:
+ (WebCore::mediaRemoteCommandForPlatformCommand):
+ (WebCore::RemoteCommandListenerMac::defaultCommands):
+ (WebCore::isSeekCommand):
+ (WebCore::RemoteCommandListenerMac::updateSupportedCommands):
+ (WebCore::RemoteCommandListenerMac::RemoteCommandListenerMac):
+
2021-02-05 Jer Noble <[email protected]>
[Cocoa] CRASH in MediaPlayerPrivateMediaSourceAVFObjC::removeAudioRenderer()
Modified: trunk/Source/WebCore/Modules/mediasession/MediaImage.h (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaImage.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaImage.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,33 @@
String src;
String sizes;
String type;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static Optional<MediaImage> decode(Decoder&);
};
+template<class Encoder> inline void MediaImage::encode(Encoder& encoder) const
+{
+ encoder << src << sizes << type;
}
+template<class Decoder> inline Optional<MediaImage> MediaImage::decode(Decoder& decoder)
+{
+ String src;
+ if (!decoder.decode(src))
+ return { };
+
+ String sizes;
+ if (!decoder.decode(sizes))
+ return { };
+
+ String type;
+ if (!decoder.decode(type))
+ return { };
+
+ return MediaImage { WTFMove(src), WTFMove(sizes), WTFMove(type) };
+}
+
+}
+
#endif // ENABLE(MEDIA_SESSION)
Modified: trunk/Source/WebCore/Modules/mediasession/MediaMetadata.cpp (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaMetadata.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaMetadata.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -64,25 +64,28 @@
void MediaMetadata::setTitle(const String& title)
{
- if (m_title == title)
+ if (m_metadata.title == title)
return;
- m_title = title;
+
+ m_metadata.title = title;
metadataUpdated();
}
void MediaMetadata::setArtist(const String& artist)
{
- if (m_artist == artist)
+ if (m_metadata.artist == artist)
return;
- m_artist = artist;
+
+ m_metadata.artist = artist;
metadataUpdated();
}
void MediaMetadata::setAlbum(const String& album)
{
- if (m_album == album)
+ if (m_metadata.album == album)
return;
- m_album = album;
+
+ m_metadata.album = album;
metadataUpdated();
}
@@ -97,7 +100,7 @@
resolvedArtwork.uncheckedAppend(MediaImage { resolvedSrc.string(), image.sizes, image.type });
}
- m_artwork = WTFMove(resolvedArtwork);
+ m_metadata.artwork = WTFMove(resolvedArtwork);
metadataUpdated();
return { };
}
Modified: trunk/Source/WebCore/Modules/mediasession/MediaMetadata.h (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaMetadata.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaMetadata.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,6 +27,7 @@
#if ENABLE(MEDIA_SESSION)
+#include "MediaMetadataInit.h"
#include "MediaSession.h"
#include <wtf/Optional.h>
#include <wtf/Vector.h>
@@ -34,11 +35,10 @@
namespace WebCore {
-class Document;
-class MediaSession;
struct MediaImage;
-struct MediaMetadataInit;
+using MediaSessionMetadata = MediaMetadataInit;
+
class MediaMetadata : public RefCounted<MediaMetadata> {
public:
static ExceptionOr<Ref<MediaMetadata>> create(ScriptExecutionContext&, Optional<MediaMetadataInit>&&);
@@ -47,27 +47,26 @@
void setMediaSession(MediaSession&);
void resetMediaSession();
- const String& title() const { return m_title; }
+ const String& title() const { return m_metadata.title; }
void setTitle(const String&);
- const String& artist() const { return m_artist; }
+ const String& artist() const { return m_metadata.artist; }
void setArtist(const String&);
- const String& album() const { return m_album; }
+ const String& album() const { return m_metadata.album; }
void setAlbum(const String&);
- const Vector<MediaImage>& artwork() const { return m_artwork; }
+ const Vector<MediaImage>& artwork() const { return m_metadata.artwork; }
ExceptionOr<void> setArtwork(ScriptExecutionContext&, Vector<MediaImage>&&);
+ const MediaSessionMetadata& metadata() const { return m_metadata; }
+
private:
MediaMetadata();
void metadataUpdated();
WeakPtr<MediaSession> m_session;
- String m_title;
- String m_artist;
- String m_album;
- Vector<MediaImage> m_artwork;
+ MediaSessionMetadata m_metadata;
};
}
Modified: trunk/Source/WebCore/Modules/mediasession/MediaMetadataInit.h (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaMetadataInit.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaMetadataInit.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,8 +36,37 @@
String artist;
String album;
Vector<MediaImage> artwork;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static Optional<MediaMetadataInit> decode(Decoder&);
};
+template<class Encoder> inline void MediaMetadataInit::encode(Encoder& encoder) const
+{
+ encoder << title << artist << album << artwork;
}
+template<class Decoder> inline Optional<MediaMetadataInit> MediaMetadataInit::decode(Decoder& decoder)
+{
+ String title;
+ if (!decoder.decode(title))
+ return { };
+
+ String artist;
+ if (!decoder.decode(artist))
+ return { };
+
+ String album;
+ if (!decoder.decode(album))
+ return { };
+
+ Vector<MediaImage> artwork;
+ if (!decoder.decode(artwork))
+ return { };
+
+ return MediaMetadataInit { WTFMove(title), WTFMove(artist), WTFMove(album), WTFMove(artwork) };
+}
+
+}
+
#endif // ENABLE(MEDIA_SESSION)
Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,11 +28,46 @@
#if ENABLE(MEDIA_SESSION)
+#include "Logging.h"
#include "MediaMetadata.h"
#include "Navigator.h"
+#include "PlatformMediaSessionManager.h"
namespace WebCore {
+static const void* nextLogIdentifier()
+{
+ static uint64_t logIdentifier = cryptographicallyRandomNumber();
+ return reinterpret_cast<const void*>(++logIdentifier);
+}
+
+static WTFLogChannel& logChannel() { return LogMedia; }
+static const char* logClassName() { return "MediaSession"; }
+
+static PlatformMediaSession::RemoteControlCommandType platformCommandForMediaSessionAction(MediaSessionAction action)
+{
+ static const auto commandMap = makeNeverDestroyed([] {
+ using ActionToCommandMap = HashMap<MediaSessionAction, PlatformMediaSession::RemoteControlCommandType, WTF::IntHash<MediaSessionAction>, WTF::StrongEnumHashTraits<MediaSessionAction>>;
+
+ return ActionToCommandMap {
+ { MediaSessionAction::Play, PlatformMediaSession::PlayCommand },
+ { MediaSessionAction::Pause, PlatformMediaSession::PauseCommand },
+ { MediaSessionAction::Seekforward, PlatformMediaSession::SkipForwardCommand },
+ { MediaSessionAction::Seekbackward, PlatformMediaSession::SkipBackwardCommand },
+ { MediaSessionAction::Previoustrack, PlatformMediaSession::NextTrackCommand },
+ { MediaSessionAction::Nexttrack, PlatformMediaSession::PreviousTrackCommand },
+ { MediaSessionAction::Stop, PlatformMediaSession::StopCommand },
+ { MediaSessionAction::Seekto, PlatformMediaSession::SeekToPlaybackPositionCommand },
+ };
+ }());
+
+ auto it = commandMap.get().find(action);
+ if (it != commandMap.get().end())
+ return it->value;
+
+ return PlatformMediaSession::NoCommand;
+}
+
Ref<MediaSession> MediaSession::create(Navigator& navigator)
{
return adoptRef(*new MediaSession(navigator));
@@ -41,6 +76,10 @@
MediaSession::MediaSession(Navigator& navigator)
: m_navigator(makeWeakPtr(navigator))
{
+ m_logger = makeRefPtr(Document::sharedLogger());
+ m_logIdentifier = nextLogIdentifier();
+
+ ALWAYS_LOG(LOGIDENTIFIER);
}
MediaSession::~MediaSession() = default;
@@ -47,6 +86,8 @@
void MediaSession::setMetadata(RefPtr<MediaMetadata>&& metadata)
{
+ ALWAYS_LOG(LOGIDENTIFIER);
+
if (m_metadata)
m_metadata->resetMediaSession();
m_metadata = WTFMove(metadata);
@@ -60,6 +101,8 @@
if (m_playbackState == state)
return;
+ ALWAYS_LOG(LOGIDENTIFIER, state);
+
auto currentPosition = this->currentPosition();
if (m_positionState && currentPosition) {
m_positionState->position = *currentPosition;
@@ -70,10 +113,18 @@
void MediaSession::setActionHandler(MediaSessionAction action, RefPtr<MediaSessionActionHandler>&& handler)
{
- if (handler)
+ if (handler) {
+ ALWAYS_LOG(LOGIDENTIFIER, "adding ", action);
m_actionHandlers.set(action, handler);
- else
- m_actionHandlers.remove(action);
+ PlatformMediaSessionManager::sharedManager().addSupportedCommand(platformCommandForMediaSessionAction(action));
+ } else {
+ if (m_actionHandlers.contains(action)) {
+ ALWAYS_LOG(LOGIDENTIFIER, "removing ", action);
+ m_actionHandlers.remove(action);
+ }
+ PlatformMediaSessionManager::sharedManager().removeSupportedCommand(platformCommandForMediaSessionAction(action));
+ }
+
actionHandlersUpdated();
}
@@ -89,6 +140,8 @@
ExceptionOr<void> MediaSession::setPositionState(Optional<MediaPositionState>&& state)
{
+ ALWAYS_LOG(LOGIDENTIFIER);
+
if (!state) {
m_positionState = WTF::nullopt;
return { };
Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.h (272444 => 272445)
--- trunk/Source/WebCore/Modules/mediasession/MediaSession.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,10 +27,14 @@
#if ENABLE(MEDIA_SESSION)
+#include "JSMediaPositionState.h"
+#include "JSMediaSessionAction.h"
+#include "JSMediaSessionPlaybackState.h"
#include "MediaPositionState.h"
#include "MediaSessionAction.h"
#include "MediaSessionActionHandler.h"
#include "MediaSessionPlaybackState.h"
+#include <wtf/Logger.h>
#include <wtf/MonotonicTime.h>
#include <wtf/Optional.h>
#include <wtf/UniqueRef.h>
@@ -57,13 +61,18 @@
WEBCORE_EXPORT Optional<double> currentPosition() const;
void metadataUpdated();
+
void actionHandlersUpdated();
bool hasActionHandler(MediaSessionAction) const;
WEBCORE_EXPORT RefPtr<MediaSessionActionHandler> handlerForAction(MediaSessionAction) const;
+ bool hasActiveActionHandlers() const { return !m_actionHandlers.isEmpty(); }
private:
explicit MediaSession(Navigator&);
+ const Logger& logger() const { return *m_logger.get(); }
+ const void* logIdentifier() const { return m_logIdentifier; }
+
WeakPtr<Navigator> m_navigator;
RefPtr<MediaMetadata> m_metadata;
MediaSessionPlaybackState m_playbackState { MediaSessionPlaybackState::None };
@@ -71,8 +80,22 @@
Optional<double> m_lastReportedPosition;
MonotonicTime m_timeAtLastPositionUpdate;
HashMap<MediaSessionAction, RefPtr<MediaSessionActionHandler>, WTF::IntHash<MediaSessionAction>, WTF::StrongEnumHashTraits<MediaSessionAction>> m_actionHandlers;
+ RefPtr<const Logger> m_logger;
+ const void* m_logIdentifier;
};
}
+namespace WTF {
+
+template<> struct LogArgument<WebCore::MediaSessionPlaybackState> {
+ static String toString(WebCore::MediaSessionPlaybackState state) { return convertEnumerationToString(state); }
+};
+
+template<> struct LogArgument<WebCore::MediaSessionAction> {
+ static String toString(WebCore::MediaSessionAction action) { return convertEnumerationToString(action); }
+};
+
+}
+
#endif // ENABLE(MEDIA_SESSION)
Modified: trunk/Source/WebCore/PAL/ChangeLog (272444 => 272445)
--- trunk/Source/WebCore/PAL/ChangeLog 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/PAL/ChangeLog 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,3 +1,13 @@
+2021-02-05 Eric Carlson <[email protected]>
+
+ [Mac] Connect MediaSession with MediaRemote and NowPlaying
+ https://bugs.webkit.org/show_bug.cgi?id=221431
+ <rdar://problem/74000363>
+
+ Reviewed by Jer Noble.
+
+ * pal/spi/mac/MediaRemoteSPI.h:
+
2021-02-01 Jer Noble <[email protected]>
[Cocoa] Disable interstitial events on AVPlayerItem.
Modified: trunk/Source/WebCore/PAL/pal/spi/mac/MediaRemoteSPI.h (272444 => 272445)
--- trunk/Source/WebCore/PAL/pal/spi/mac/MediaRemoteSPI.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/PAL/pal/spi/mac/MediaRemoteSPI.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -92,6 +92,8 @@
typedef uint32_t MRNowPlayingClientVisibility;
typedef uint32_t MRMediaRemoteError;
+typedef uint32_t MRSendCommandAppOptions;
+typedef uint32_t MRSendCommandError;
typedef struct _MROrigin *MROriginRef;
typedef struct _MRMediaRemoteCommandInfo *MRMediaRemoteCommandInfoRef;
typedef void *MRNowPlayingClientRef;
@@ -103,8 +105,10 @@
void* MRMediaRemoteAddAsyncCommandHandlerBlock(MRMediaRemoteAsyncCommandHandlerBlock);
void MRMediaRemoteRemoveCommandHandlerBlock(void *observer);
-void MRMediaRemoteSetSupportedCommands(CFArrayRef commands, MROriginRef, dispatch_queue_t replyQ, void(^completion)(MRMediaRemoteError err));
+void MRMediaRemoteSetSupportedCommands(CFArrayRef, MROriginRef, dispatch_queue_t, void(^completion)(MRMediaRemoteError));
+void MRMediaRemoteGetSupportedCommandsForOrigin(MROriginRef, dispatch_queue_t, void(^completion)(CFArrayRef));
void MRMediaRemoteSetNowPlayingVisibility(MROriginRef, MRNowPlayingClientVisibility);
+Boolean MRMediaRemoteSendCommandToApp(MRMediaRemoteCommand, CFDictionaryRef, MROriginRef, CFStringRef, MRSendCommandAppOptions, dispatch_queue_t, void(^completion)(MRSendCommandError, CFArrayRef));
#pragma mark - MROrigin
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (272444 => 272445)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-02-05 23:18:55 UTC (rev 272445)
@@ -139,6 +139,10 @@
077AF14318F4B1BB0001ED61 /* SerializedPlatformDataCueMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 077AF14118F4B1BB0001ED61 /* SerializedPlatformDataCueMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
077B64131B94F12E003E9AD5 /* MediaPlaybackTargetPickerMock.h in Headers */ = {isa = PBXBuildFile; fileRef = 077B64111B94F12E003E9AD5 /* MediaPlaybackTargetPickerMock.h */; settings = {ATTRIBUTES = (Private, ); }; };
077B64171B95F703003E9AD5 /* MediaPlaybackTargetMock.h in Headers */ = {isa = PBXBuildFile; fileRef = 077B64151B95F703003E9AD5 /* MediaPlaybackTargetMock.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0782894825C23FE500A7BA03 /* MediaMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDDEA2D2538CE0400A1300C /* MediaMetadata.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0782894A25C36FCF00A7BA03 /* MediaSession.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDDEA232538CD8000A1300C /* MediaSession.h */; };
+ 0782894B25C36FED00A7BA03 /* MediaImage.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDDEA322538CE3500A1300C /* MediaImage.h */; };
+ 0782894C25C3700B00A7BA03 /* MediaMetadataInit.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDDEA302538CE1E00A1300C /* MediaMetadataInit.h */; };
0783228518013ED800999E0C /* MediaStreamAudioSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0783228318013ED800999E0C /* MediaStreamAudioSource.h */; };
07846343145B151A00A58DF1 /* JSTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 07846341145B151A00A58DF1 /* JSTrackEvent.h */; };
07846385145B1B8E00A58DF1 /* JSTrackCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 07846384145B1B8E00A58DF1 /* JSTrackCustom.h */; };
@@ -33702,10 +33706,13 @@
E44613AD0CD6331000FADA75 /* MediaError.h in Headers */,
4E1959220A39DABA00220FE5 /* MediaFeatureNames.h in Headers */,
07A6D1EC1491137700051D0C /* MediaFragmentURIParser.h in Headers */,
+ 0782894B25C36FED00A7BA03 /* MediaImage.h in Headers */,
CDF4B71A1E01D3D000E235A2 /* MediaKeySessionType.idl in Headers */,
CDF4B7281E03C57300E235A2 /* MediaKeysRequirement.idl in Headers */,
CDF4B7181E01CB9100E235A2 /* MediaKeysRestrictions.h in Headers */,
A8EA800E0A19516E00A8EF5F /* MediaList.h in Headers */,
+ 0782894825C23FE500A7BA03 /* MediaMetadata.h in Headers */,
+ 0782894C25C3700B00A7BA03 /* MediaMetadataInit.h in Headers */,
5EBB89311C7777FF00C65D41 /* MediaPayload.h in Headers */,
07E3DFD11A9E786500764CA8 /* MediaPlaybackTarget.h in Headers */,
079216551AA560AA00A3C049 /* MediaPlaybackTargetClient.h in Headers */,
@@ -33746,6 +33753,7 @@
1B124D8D1D380B7000ECDFB0 /* MediaSampleAVFObjC.h in Headers */,
CDBEAEAD19D92B6C00BEBA88 /* MediaSelectionGroupAVFObjC.h in Headers */,
A17D275E1EAC579800BF01E7 /* MediaSelectionOption.h in Headers */,
+ 0782894A25C36FCF00A7BA03 /* MediaSession.h in Headers */,
416ECAE525B58CC400B34DA5 /* MediaSessionGroupIdentifier.h in Headers */,
CDA9593F2412BAE000910EEF /* MediaSessionHelperIOS.h in Headers */,
414460A22412994500814BE7 /* MediaSessionIdentifier.h in Headers */,
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (272444 => 272445)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -7519,6 +7519,7 @@
ALWAYS_LOG(LOGIDENTIFIER, command);
UserGestureIndicator remoteControlUserGesture(ProcessingUserGesture, &document());
+ double offset = 15;
switch (command) {
case PlatformMediaSession::PlayCommand:
play();
@@ -7540,6 +7541,16 @@
case PlatformMediaSession::EndSeekingForwardCommand:
endScanning();
break;
+ case PlatformMediaSession::SkipForwardCommand:
+ if (argument)
+ offset = argument->asDouble;
+ handleSeekToPlaybackPosition(offset);
+ break;
+ case PlatformMediaSession::SkipBackwardCommand:
+ if (argument)
+ offset = argument->asDouble;
+ handleSeekToPlaybackPosition(0 - offset);
+ break;
case PlatformMediaSession::SeekToPlaybackPositionCommand:
ASSERT(argument);
if (argument)
Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (272444 => 272445)
--- trunk/Source/WebCore/html/MediaElementSession.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -54,6 +54,12 @@
#include "SourceBuffer.h"
#include <wtf/text/StringBuilder.h>
+#if ENABLE(MEDIA_SESSION)
+#include "MediaMetadata.h"
+#include "MediaSession.h"
+#include "NavigatorMediaSession.h"
+#endif
+
#if PLATFORM(IOS_FAMILY)
#include "AudioSession.h"
#include "RuntimeApplicationChecks.h"
@@ -1021,6 +1027,70 @@
return page && page->allowsPlaybackControlsForAutoplayingAudio();
}
+#if ENABLE(MEDIA_SESSION)
+void MediaElementSession::didReceiveRemoteControlCommand(RemoteControlCommandType commandType, const RemoteCommandArgument* argument)
+{
+ auto* window = m_element.document().domWindow();
+ auto* session = window ? &NavigatorMediaSession::mediaSession(window->navigator()) : nullptr;
+ if (!session || !session->hasActiveActionHandlers()) {
+ PlatformMediaSession::didReceiveRemoteControlCommand(commandType, argument);
+ return;
+ }
+
+ MediaSessionActionDetails actionDetails;
+ switch (commandType) {
+ case NoCommand:
+ return;
+ case PlayCommand:
+ actionDetails.action = ""
+ break;
+ case PauseCommand:
+ actionDetails.action = ""
+ break;
+ case StopCommand:
+ actionDetails.action = ""
+ break;
+ case TogglePlayPauseCommand:
+ actionDetails.action = "" ? MediaSessionAction::Play : MediaSessionAction::Pause;
+ break;
+ case SeekToPlaybackPositionCommand:
+ ASSERT(argument);
+ if (!argument)
+ return;
+ actionDetails.action = ""
+ actionDetails.seekTime = argument->asDouble;
+ break;
+ case SkipForwardCommand:
+ if (argument)
+ actionDetails.seekOffset = argument->asDouble;
+ actionDetails.action = ""
+ break;
+ case SkipBackwardCommand:
+ if (argument)
+ actionDetails.seekOffset = argument->asDouble;
+ actionDetails.action = ""
+ break;
+ case NextTrackCommand:
+ actionDetails.action = ""
+ break;
+ case PreviousTrackCommand:
+ actionDetails.action = ""
+ break;
+ case BeginSeekingBackwardCommand:
+ case EndSeekingBackwardCommand:
+ case BeginSeekingForwardCommand:
+ case EndSeekingForwardCommand:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ if (auto handler = session->handlerForAction(actionDetails.action))
+ handler->handleEvent(actionDetails);
+ else
+ ALWAYS_LOG(LOGIDENTIFIER, "Ignoring command, no action handler registered for ", actionDetails.action);
+}
+#endif
+
Optional<NowPlayingInfo> MediaElementSession::nowPlayingInfo() const
{
auto* page = m_element.document().page();
@@ -1032,7 +1102,14 @@
if (!std::isfinite(currentTime) || !supportsSeeking)
currentTime = MediaPlayer::invalidTime();
- return NowPlayingInfo { m_element.mediaSessionTitle(), m_element.sourceApplicationIdentifier(), duration, currentTime, supportsSeeking, m_element.mediaSessionUniqueIdentifier(), isPlaying, allowsNowPlayingControlsVisibility };
+#if ENABLE(MEDIA_SESSION)
+ auto* window = m_element.document().domWindow();
+ auto* sessionMetadata = window ? NavigatorMediaSession::mediaSession(window->navigator()).metadata() : nullptr;
+ if (sessionMetadata)
+ return NowPlayingInfo { sessionMetadata->title(), sessionMetadata->artist(), sessionMetadata->album(), m_element.sourceApplicationIdentifier(), duration, currentTime, supportsSeeking, m_element.mediaSessionUniqueIdentifier(), isPlaying, allowsNowPlayingControlsVisibility };
+#endif
+
+ return NowPlayingInfo { m_element.mediaSessionTitle(), emptyString(), emptyString(), m_element.sourceApplicationIdentifier(), duration, currentTime, supportsSeeking, m_element.mediaSessionUniqueIdentifier(), isPlaying, allowsNowPlayingControlsVisibility };
}
void MediaElementSession::updateMediaUsageIfChanged()
Modified: trunk/Source/WebCore/html/MediaElementSession.h (272444 => 272445)
--- trunk/Source/WebCore/html/MediaElementSession.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/html/MediaElementSession.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -174,6 +174,10 @@
const char* logClassName() const final { return "MediaElementSession"; }
#endif
+#if ENABLE(MEDIA_SESSION)
+ void didReceiveRemoteControlCommand(RemoteControlCommandType, const RemoteCommandArgument* = nullptr) final;
+#endif
+
private:
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
Modified: trunk/Source/WebCore/page/ChromeClient.h (272444 => 272445)
--- trunk/Source/WebCore/page/ChromeClient.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/page/ChromeClient.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -101,7 +101,6 @@
class HTMLVideoElement;
class HitTestResult;
class IntRect;
-class MediaSessionMetadata;
class NavigationAction;
class Node;
class Page;
Modified: trunk/Source/WebCore/platform/RemoteCommandListener.cpp (272444 => 272445)
--- trunk/Source/WebCore/platform/RemoteCommandListener.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/RemoteCommandListener.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,4 +37,25 @@
#endif
+void RemoteCommandListener::scheduleSupportedCommandsUpdate()
+{
+ if (!m_updateCommandsTask.hasPendingTask()) {
+ m_updateCommandsTask.scheduleTask([this] () {
+ updateSupportedCommands();
+ });
+ }
}
+
+void RemoteCommandListener::addSupportedCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ m_registeredCommands.add(command);
+ scheduleSupportedCommandsUpdate();
+}
+
+void RemoteCommandListener::removeSupportedCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ m_registeredCommands.remove(command);
+ scheduleSupportedCommandsUpdate();
+}
+
+}
Modified: trunk/Source/WebCore/platform/RemoteCommandListener.h (272444 => 272445)
--- trunk/Source/WebCore/platform/RemoteCommandListener.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/RemoteCommandListener.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
#ifndef RemoteCommandListener_h
#define RemoteCommandListener_h
+#include "DeferrableTask.h"
#include "PlatformMediaSession.h"
namespace WebCore {
@@ -45,12 +46,20 @@
RemoteCommandListener(RemoteCommandListenerClient& client) : m_client(client) { }
virtual ~RemoteCommandListener() = default;
+ void addSupportedCommand(PlatformMediaSession::RemoteControlCommandType);
+ void removeSupportedCommand(PlatformMediaSession::RemoteControlCommandType);
virtual void updateSupportedCommands() { }
+ void scheduleSupportedCommandsUpdate();
RemoteCommandListenerClient& client() const { return m_client; }
protected:
RemoteCommandListenerClient& m_client;
+
+ using RemoteCommandsSet = HashSet<PlatformMediaSession::RemoteControlCommandType, WTF::IntHash<PlatformMediaSession::RemoteControlCommandType>, WTF::StrongEnumHashTraits<PlatformMediaSession::RemoteControlCommandType>>;
+ RemoteCommandsSet m_registeredCommands;
+
+ DeferrableTask<Timer> m_updateCommandsTask;
};
}
Modified: trunk/Source/WebCore/platform/audio/NowPlayingInfo.h (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/NowPlayingInfo.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/NowPlayingInfo.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,6 +32,8 @@
struct NowPlayingInfo {
String title;
+ String artist;
+ String album;
String sourceApplicationIdentifier;
double duration { 0 };
double currentTime { 0 };
@@ -55,6 +57,14 @@
if (!decoder.decode(title))
return { };
+ String artist;
+ if (!decoder.decode(artist))
+ return { };
+
+ String album;
+ if (!decoder.decode(album))
+ return { };
+
String sourceApplicationIdentifier;
if (!decoder.decode(sourceApplicationIdentifier))
return { };
@@ -83,7 +93,7 @@
if (!decoder.decode(allowsNowPlayingControlsVisibility))
return { };
- return NowPlayingInfo { WTFMove(title), WTFMove(sourceApplicationIdentifier), duration, currentTime, supportsSeeking, uniqueIdentifier, isPlaying, allowsNowPlayingControlsVisibility };
+ return NowPlayingInfo { WTFMove(title), WTFMove(artist), WTFMove(album), WTFMove(sourceApplicationIdentifier), duration, currentTime, supportsSeeking, uniqueIdentifier, isPlaying, allowsNowPlayingControlsVisibility };
}
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -93,6 +93,10 @@
MAKE_STATIC_STRING_IMPL("BeginSeekingForwardCommand"),
MAKE_STATIC_STRING_IMPL("EndSeekingForwardCommand"),
MAKE_STATIC_STRING_IMPL("SeekToPlaybackPositionCommand"),
+ MAKE_STATIC_STRING_IMPL("SkipForwardCommand"),
+ MAKE_STATIC_STRING_IMPL("SkipBackwardCommand"),
+ MAKE_STATIC_STRING_IMPL("NextTrackCommand"),
+ MAKE_STATIC_STRING_IMPL("PreviousTrackCommand"),
};
static_assert(!static_cast<size_t>(PlatformMediaSession::NoCommand), "PlatformMediaSession::NoCommand is not 0 as expected");
static_assert(static_cast<size_t>(PlatformMediaSession::PlayCommand) == 1, "PlatformMediaSession::PlayCommand is not 1 as expected");
@@ -104,6 +108,10 @@
static_assert(static_cast<size_t>(PlatformMediaSession::BeginSeekingForwardCommand) == 7, "PlatformMediaSession::BeginSeekingForwardCommand is not 7 as expected");
static_assert(static_cast<size_t>(PlatformMediaSession::EndSeekingForwardCommand) == 8, "PlatformMediaSession::EndSeekingForwardCommand is not 8 as expected");
static_assert(static_cast<size_t>(PlatformMediaSession::SeekToPlaybackPositionCommand) == 9, "PlatformMediaSession::SeekToPlaybackPositionCommand is not 9 as expected");
+ static_assert(static_cast<size_t>(PlatformMediaSession::SkipForwardCommand) == 10, "PlatformMediaSession::SkipForwardCommand is not 10 as expected");
+ static_assert(static_cast<size_t>(PlatformMediaSession::SkipBackwardCommand) == 11, "PlatformMediaSession::SkipBackwardCommand is not 11 as expected");
+ static_assert(static_cast<size_t>(PlatformMediaSession::NextTrackCommand) == 12, "PlatformMediaSession::NextTrackCommand is not 12 as expected");
+ static_assert(static_cast<size_t>(PlatformMediaSession::PreviousTrackCommand) == 13, "PlatformMediaSession::PreviousTrackCommand is not 13 as expected");
ASSERT(static_cast<size_t>(command) < WTF_ARRAY_LENGTH(values));
return values[static_cast<size_t>(command)];
}
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.h (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -130,9 +130,13 @@
BeginSeekingForwardCommand,
EndSeekingForwardCommand,
SeekToPlaybackPositionCommand,
+ SkipForwardCommand,
+ SkipBackwardCommand,
+ NextTrackCommand,
+ PreviousTrackCommand,
};
bool canReceiveRemoteControlCommands() const;
- void didReceiveRemoteControlCommand(RemoteControlCommandType, const RemoteCommandArgument* argument = nullptr);
+ virtual void didReceiveRemoteControlCommand(RemoteControlCommandType, const RemoteCommandArgument* = nullptr);
bool supportsSeeking() const;
enum DisplayType : uint8_t {
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -152,6 +152,9 @@
bool isInterrupted() const { return m_interrupted; }
bool hasNoSession() const;
+ virtual void addSupportedCommand(PlatformMediaSession::RemoteControlCommandType) { };
+ virtual void removeSupportedCommand(PlatformMediaSession::RemoteControlCommandType) { };
+
protected:
friend class PlatformMediaSession;
PlatformMediaSessionManager();
Modified: trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.h (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -84,6 +84,9 @@
GenericTaskQueue<Timer>& taskQueue() { return m_taskQueue; }
+ void addSupportedCommand(PlatformMediaSession::RemoteControlCommandType) final;
+ void removeSupportedCommand(PlatformMediaSession::RemoteControlCommandType) final;
+
private:
#if !RELEASE_LOG_DISABLED
const char* logClassName() const override { return "MediaSessionManagerCocoa"; }
Modified: trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm (272444 => 272445)
--- trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -153,6 +153,9 @@
void MediaSessionManagerCocoa::scheduleSessionStatusUpdate()
{
m_taskQueue.enqueueTask([this] () mutable {
+ if (m_remoteCommandListener)
+ m_remoteCommandListener->updateSupportedCommands();
+
updateNowPlayingInfo();
forEachSession([] (auto& session) {
@@ -232,10 +235,23 @@
void MediaSessionManagerCocoa::sessionCanProduceAudioChanged()
{
+ ALWAYS_LOG(LOGIDENTIFIER);
PlatformMediaSessionManager::sessionCanProduceAudioChanged();
scheduleSessionStatusUpdate();
}
+void MediaSessionManagerCocoa::addSupportedCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ if (m_remoteCommandListener)
+ m_remoteCommandListener->addSupportedCommand(command);
+}
+
+void MediaSessionManagerCocoa::removeSupportedCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ if (m_remoteCommandListener)
+ m_remoteCommandListener->removeSupportedCommand(command);
+}
+
void MediaSessionManagerCocoa::clearNowPlayingInfo()
{
if (canLoad_MediaRemote_MRMediaRemoteSetNowPlayingVisibility())
@@ -260,6 +276,12 @@
auto info = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ if (!nowPlayingInfo.artist.isEmpty())
+ CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoArtist, nowPlayingInfo.artist.createCFString().get());
+
+ if (!nowPlayingInfo.album.isEmpty())
+ CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoAlbum, nowPlayingInfo.album.createCFString().get());
+
if (!nowPlayingInfo.title.isEmpty())
CFDictionarySetValue(info.get(), kMRMediaRemoteNowPlayingInfoTitle, nowPlayingInfo.title.createCFString().get());
Modified: trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.h (272444 => 272445)
--- trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -58,6 +58,14 @@
#define MRMediaRemoteSetParentApplication softLink_MediaRemote_MRMediaRemoteSetParentApplication
SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoTitle, CFStringRef);
#define kMRMediaRemoteNowPlayingInfoTitle get_MediaRemote_kMRMediaRemoteNowPlayingInfoTitle()
+SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtist, CFStringRef);
+#define kMRMediaRemoteNowPlayingInfoArtist get_MediaRemote_kMRMediaRemoteNowPlayingInfoArtist()
+SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoAlbum, CFStringRef);
+#define kMRMediaRemoteNowPlayingInfoAlbum get_MediaRemote_kMRMediaRemoteNowPlayingInfoAlbum()
+SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtworkData, CFStringRef);
+#define kMRMediaRemoteNowPlayingInfoArtworkData get_MediaRemote_kMRMediaRemoteNowPlayingInfoArtworkData()
+SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtworkMIMEType, CFStringRef);
+#define kMRMediaRemoteNowPlayingInfoArtworkMIMEType get_MediaRemote_kMRMediaRemoteNowPlayingInfoArtworkMIMEType()
SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoDuration, CFStringRef);
#define kMRMediaRemoteNowPlayingInfoDuration get_MediaRemote_kMRMediaRemoteNowPlayingInfoDuration()
SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoElapsedTime, CFStringRef);
@@ -68,6 +76,8 @@
#define kMRMediaRemoteOptionPlaybackPosition get_MediaRemote_kMRMediaRemoteOptionPlaybackPosition()
SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoUniqueIdentifier, CFStringRef);
#define kMRMediaRemoteNowPlayingInfoUniqueIdentifier get_MediaRemote_kMRMediaRemoteNowPlayingInfoUniqueIdentifier()
+SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaRemote, kMRMediaRemoteOptionSkipInterval, CFStringRef);
+#define kMRMediaRemoteOptionSkipInterval get_MediaRemote_kMRMediaRemoteOptionSkipInterval()
#if PLATFORM(IOS_FAMILY)
SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, MediaRemote, MRMediaRemoteCopyPickableRoutes, CFArrayRef, (), ())
Modified: trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.mm (272444 => 272445)
--- trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.mm 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/mac/MediaRemoteSoftLink.mm 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -44,11 +44,16 @@
SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaRemote, MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin, void, (MROriginRef origin, MRPlaybackState playbackState, dispatch_queue_t replyQ, void(^completion)(MRMediaRemoteError)), (origin, playbackState, replyQ, completion))
SOFT_LINK_FUNCTION_MAY_FAIL_FOR_SOURCE(WebCore, MediaRemote, MRMediaRemoteSetParentApplication, void, (MROriginRef origin, CFStringRef parentAppDisplayID), (origin, parentAppDisplayID))
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoTitle, CFStringRef);
+SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtist, CFStringRef);
+SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoAlbum, CFStringRef);
+SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtworkData, CFStringRef);
+SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoArtworkMIMEType, CFStringRef);
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoDuration, CFStringRef);
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoElapsedTime, CFStringRef);
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoPlaybackRate, CFStringRef);
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteOptionPlaybackPosition, CFStringRef);
SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteNowPlayingInfoUniqueIdentifier, CFStringRef);
+SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaRemote, kMRMediaRemoteOptionSkipInterval, CFStringRef);
#if PLATFORM(IOS_FAMILY)
SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaRemote, MRMediaRemoteCopyPickableRoutes, CFArrayRef, (), ());
Modified: trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.h (272444 => 272445)
--- trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -39,9 +39,13 @@
virtual ~RemoteCommandListenerMac();
protected:
- void updateSupportedCommands() override;
+ void updateSupportedCommands() final;
void* m_commandHandler { nullptr };
+
+ const RemoteCommandsSet& defaultCommands();
+ RemoteCommandsSet m_currentCommands;
+ bool m_supportsSeeking { false };
};
}
Modified: trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.mm (272444 => 272445)
--- trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.mm 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebCore/platform/mac/RemoteCommandListenerMac.mm 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,42 +35,95 @@
namespace WebCore {
+static Optional<MRMediaRemoteCommand> mediaRemoteCommandForPlatformCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ static const auto commandMap = makeNeverDestroyed([] {
+ using CommandToActionMap = HashMap<PlatformMediaSession::RemoteControlCommandType, MRMediaRemoteCommand, WTF::IntHash<PlatformMediaSession::RemoteControlCommandType>, WTF::StrongEnumHashTraits<PlatformMediaSession::RemoteControlCommandType>>;
+
+ return CommandToActionMap {
+ { PlatformMediaSession::PlayCommand, MRMediaRemoteCommandPlay },
+ { PlatformMediaSession::PauseCommand, MRMediaRemoteCommandPause },
+ { PlatformMediaSession::StopCommand, MRMediaRemoteCommandStop },
+ { PlatformMediaSession::TogglePlayPauseCommand, MRMediaRemoteCommandTogglePlayPause },
+ { PlatformMediaSession::BeginSeekingBackwardCommand, MRMediaRemoteCommandBeginRewind },
+ { PlatformMediaSession::EndSeekingBackwardCommand, MRMediaRemoteCommandEndRewind },
+ { PlatformMediaSession::BeginSeekingForwardCommand, MRMediaRemoteCommandBeginFastForward },
+ { PlatformMediaSession::EndSeekingForwardCommand, MRMediaRemoteCommandEndFastForward },
+ { PlatformMediaSession::SeekToPlaybackPositionCommand, MRMediaRemoteCommandSeekToPlaybackPosition },
+ { PlatformMediaSession::SkipForwardCommand, MRMediaRemoteCommandSkipForward },
+ { PlatformMediaSession::SkipBackwardCommand, MRMediaRemoteCommandSkipBackward },
+ { PlatformMediaSession::NextTrackCommand, MRMediaRemoteCommandNextTrack },
+ { PlatformMediaSession::PreviousTrackCommand, MRMediaRemoteCommandPreviousTrack },
+ };
+ }());
+
+ auto it = commandMap.get().find(command);
+ if (it != commandMap.get().end())
+ return { it->value };
+
+ return { };
+}
+
std::unique_ptr<RemoteCommandListener> RemoteCommandListener::create(RemoteCommandListenerClient& client)
{
return makeUnique<RemoteCommandListenerMac>(client);
}
+const RemoteCommandListener::RemoteCommandsSet& RemoteCommandListenerMac::defaultCommands()
+{
+ static NeverDestroyed<RemoteCommandsSet> commands(std::initializer_list<PlatformMediaSession::RemoteControlCommandType> {
+ PlatformMediaSession::PlayCommand,
+ PlatformMediaSession::PauseCommand,
+ PlatformMediaSession::TogglePlayPauseCommand,
+ PlatformMediaSession::BeginSeekingForwardCommand,
+ PlatformMediaSession::EndSeekingForwardCommand,
+ PlatformMediaSession::BeginSeekingBackwardCommand,
+ PlatformMediaSession::EndSeekingBackwardCommand,
+ PlatformMediaSession::SeekToPlaybackPositionCommand,
+ PlatformMediaSession::SkipForwardCommand,
+ PlatformMediaSession::SkipBackwardCommand,
+ });
+
+ return commands;
+}
+
+static bool isSeekCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ return command == PlatformMediaSession::SeekToPlaybackPositionCommand
+ || command == PlatformMediaSession::SkipForwardCommand
+ || command == PlatformMediaSession::SkipBackwardCommand
+ || command == PlatformMediaSession::BeginSeekingForwardCommand
+ || command == PlatformMediaSession::BeginSeekingBackwardCommand;
+}
+
void RemoteCommandListenerMac::updateSupportedCommands()
{
if (!isMediaRemoteFrameworkAvailable())
return;
- static const MRMediaRemoteCommand supportedCommands[] = {
- MRMediaRemoteCommandPlay,
- MRMediaRemoteCommandPause,
- MRMediaRemoteCommandTogglePlayPause,
- MRMediaRemoteCommandBeginFastForward,
- MRMediaRemoteCommandEndFastForward,
- MRMediaRemoteCommandBeginRewind,
- MRMediaRemoteCommandEndRewind,
- MRMediaRemoteCommandSeekToPlaybackPosition,
- };
+ auto& supportedCommands = !m_registeredCommands.isEmpty() ? m_registeredCommands : defaultCommands();
+ if (m_supportsSeeking == client().supportsSeeking() && m_currentCommands == supportedCommands)
+ return;
- auto commandInfoArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, sizeof(supportedCommands) / sizeof(MRMediaRemoteCommand), &kCFTypeArrayCallBacks));
+ auto commandInfoArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, supportedCommands.size(), &kCFTypeArrayCallBacks));
+ for (auto platformCommand : supportedCommands) {
+ if (isSeekCommand(platformCommand) && !client().supportsSeeking())
+ continue;
- for (auto command : supportedCommands) {
+ auto command = mediaRemoteCommandForPlatformCommand(platformCommand);
+ ASSERT(command);
+ if (!command)
+ continue;
+
auto commandInfo = adoptCF(MRMediaRemoteCommandInfoCreate(kCFAllocatorDefault));
- MRMediaRemoteCommandInfoSetCommand(commandInfo.get(), command);
+ MRMediaRemoteCommandInfoSetCommand(commandInfo.get(), command.value());
MRMediaRemoteCommandInfoSetEnabled(commandInfo.get(), true);
CFArrayAppendValue(commandInfoArray.get(), commandInfo.get());
}
- auto seekCommandInfo = adoptCF(MRMediaRemoteCommandInfoCreate(kCFAllocatorDefault));
- MRMediaRemoteCommandInfoSetCommand(seekCommandInfo.get(), MRMediaRemoteCommandSeekToPlaybackPosition);
- MRMediaRemoteCommandInfoSetEnabled(seekCommandInfo.get(), client().supportsSeeking());
- CFArrayAppendValue(commandInfoArray.get(), seekCommandInfo.get());
-
MRMediaRemoteSetSupportedCommands(commandInfoArray.get(), MRMediaRemoteGetLocalOrigin(), nullptr, nullptr);
+ m_currentCommands = supportedCommands;
+ m_supportsSeeking = client().supportsSeeking();
}
RemoteCommandListenerMac::RemoteCommandListenerMac(RemoteCommandListenerClient& client)
@@ -79,7 +132,7 @@
if (!isMediaRemoteFrameworkAvailable())
return;
- updateSupportedCommands();
+ scheduleSupportedCommandsUpdate();
auto weakThis = makeWeakPtr(*this);
m_commandHandler = MRMediaRemoteAddAsyncCommandHandlerBlock(^(MRMediaRemoteCommand command, CFDictionaryRef options, void(^completion)(CFArrayRef)) {
@@ -88,6 +141,7 @@
PlatformMediaSession::RemoteControlCommandType platformCommand { PlatformMediaSession::NoCommand };
PlatformMediaSession::RemoteCommandArgument argument { 0 };
+ PlatformMediaSession::RemoteCommandArgument* argumentPtr = nullptr;
MRMediaRemoteCommandHandlerStatus status = MRMediaRemoteCommandHandlerStatusSuccess;
switch (command) {
@@ -128,9 +182,30 @@
}
CFNumberGetValue(positionRef, kCFNumberDoubleType, &argument.asDouble);
+ argumentPtr = &argument;
platformCommand = PlatformMediaSession::SeekToPlaybackPositionCommand;
break;
}
+ case MRMediaRemoteCommandSkipForward:
+ case MRMediaRemoteCommandSkipBackward:
+ if (!client.supportsSeeking()) {
+ status = MRMediaRemoteCommandHandlerStatusCommandFailed;
+ break;
+ }
+
+ if (auto positionRef = static_cast<CFNumberRef>(CFDictionaryGetValue(options, kMRMediaRemoteOptionSkipInterval))) {
+ CFNumberGetValue(positionRef, kCFNumberDoubleType, &argument.asDouble);
+ argumentPtr = &argument;
+ }
+
+ platformCommand = (command == MRMediaRemoteCommandSkipForward) ? PlatformMediaSession::SkipForwardCommand : PlatformMediaSession::SkipBackwardCommand;
+ break;
+ case MRMediaRemoteCommandNextTrack:
+ platformCommand = PlatformMediaSession::NextTrackCommand;
+ break;
+ case MRMediaRemoteCommandPreviousTrack:
+ platformCommand = PlatformMediaSession::PreviousTrackCommand;
+ break;
default:
LOG(Media, "RemoteCommandListenerMac::RemoteCommandListenerMac - command %u not supported!", command);
status = MRMediaRemoteCommandHandlerStatusCommandFailed;
@@ -137,7 +212,7 @@
};
if (weakThis && status != MRMediaRemoteCommandHandlerStatusCommandFailed)
- weakThis->m_client.didReceiveRemoteControlCommand(platformCommand, &argument);
+ weakThis->m_client.didReceiveRemoteControlCommand(platformCommand, argumentPtr);
completion((__bridge CFArrayRef)@[@(status)]);
});
Modified: trunk/Source/WebKit/ChangeLog (272444 => 272445)
--- trunk/Source/WebKit/ChangeLog 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebKit/ChangeLog 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,3 +1,18 @@
+2021-02-05 Eric Carlson <[email protected]>
+
+ [Mac] Connect MediaSession with MediaRemote and NowPlaying
+ https://bugs.webkit.org/show_bug.cgi?id=221431
+ <rdar://problem/74000363>
+
+ Reviewed by Jer Noble.
+
+ Add a private preference so the new MediaSession API test can enable the feature.
+
+ * UIProcess/API/Cocoa/WKPreferences.mm:
+ (-[WKPreferences _mediaSessionEnabled]):
+ (-[WKPreferences _setMediaSessionEnabled:]):
+ * UIProcess/API/Cocoa/WKPreferencesPrivate.h:
+
2021-02-05 Youenn Fablet <[email protected]>
Enable audio capture for speech recognition in GPUProcess
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm (272444 => 272445)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1479,6 +1479,16 @@
_preferences->setPitchCorrectionAlgorithm(pitchCorrectionAlgorithm);
}
+- (BOOL)_mediaSessionEnabled
+{
+ return _preferences->mediaSessionEnabled();
+}
+
+- (void)_setMediaSessionEnabled:(BOOL)mediaSessionEnabled
+{
+ _preferences->setMediaSessionEnabled(mediaSessionEnabled);
+}
+
@end
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h (272444 => 272445)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h 2021-02-05 23:18:55 UTC (rev 272445)
@@ -170,6 +170,7 @@
@property (nonatomic, setter=_setSpeechRecognitionEnabled:) BOOL _speechRecognitionEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
@property (nonatomic, setter=_setPrivateClickMeasurementEnabled:) BOOL _privateClickMeasurementEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
@property (nonatomic, setter=_setPitchCorrectionAlgorithm:) _WKPitchCorrectionAlgorithm _pitchCorrectionAlgorithm WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic, setter=_setMediaSessionEnabled:) BOOL _mediaSessionEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
#if !TARGET_OS_IPHONE
@property (nonatomic, setter=_setWebGLEnabled:) BOOL _webGLEnabled WK_API_AVAILABLE(macos(10.13.4));
Modified: trunk/Tools/ChangeLog (272444 => 272445)
--- trunk/Tools/ChangeLog 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Tools/ChangeLog 2021-02-05 23:18:55 UTC (rev 272445)
@@ -1,3 +1,33 @@
+2021-02-05 Eric Carlson <[email protected]>
+
+ [Mac] Connect MediaSession with MediaRemote and NowPlaying
+ https://bugs.webkit.org/show_bug.cgi?id=221431
+ <rdar://problem/74000363>
+
+ Reviewed by Jer Noble.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebKitCocoa/MediaSession.mm: Added.
+ (TestWebKitAPI::MediaSessionTest::webView):
+ (TestWebKitAPI::MediaSessionTest::webViewPid):
+ (TestWebKitAPI::MediaSessionTest::getNowPlayingClient):
+ (TestWebKitAPI::MediaSessionTest::getNowPlayingClientPid):
+ (TestWebKitAPI::MediaSessionTest::loadPageAndBecomeNowPlaying):
+ (TestWebKitAPI::MediaSessionTest::runScriptWithUserGesture):
+ (TestWebKitAPI::MediaSessionTest::play):
+ (TestWebKitAPI::MediaSessionTest::pause):
+ (TestWebKitAPI::MediaSessionTest::sendMediaRemoteCommand):
+ (TestWebKitAPI::MediaSessionTest::sendMediaRemoteSeekCommand):
+ (TestWebKitAPI::MediaSessionTest::listenForEventMessages):
+ (TestWebKitAPI::MediaSessionTest::eventListenerWasCalled):
+ (TestWebKitAPI::MediaSessionTest::waitForEventListenerToBeCalled):
+ (TestWebKitAPI::MediaSessionTest::listenForSessionHandlerMessages):
+ (TestWebKitAPI::MediaSessionTest::sessionHandlerWasCalled):
+ (TestWebKitAPI::MediaSessionTest::waitForSessionHandlerToBeCalled):
+ (TestWebKitAPI::MediaSessionTest::getSupportedCommands):
+ (TestWebKitAPI::TEST_F):
+ * TestWebKitAPI/Tests/WebKitCocoa/media-remote.html: Added.
+
2021-02-05 Sam Weinig <[email protected]>
Generalize color conversion code to reduce number of overloads required
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (272444 => 272445)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2021-02-05 23:09:46 UTC (rev 272444)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2021-02-05 23:18:55 UTC (rev 272445)
@@ -56,6 +56,8 @@
074994521EA5034B000DA44E /* getUserMedia2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 41BAF4E225AC9DB800D82F32 /* getUserMedia2.html */; };
076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 076E507E1F45031E006E9F5A /* Logging.cpp */; };
077A5AF3230638A600A7105C /* AccessibilityTestPlugin.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0746645822FF630500E3451A /* AccessibilityTestPlugin.mm */; };
+ 0794740D25CA0BDE00C597EB /* MediaSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0794740C25CA0BDE00C597EB /* MediaSession.mm */; };
+ 0794742D25CB33FD00C597EB /* media-remote.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0794742C25CB33B000C597EB /* media-remote.html */; };
0799C3491EBA2D7B003B7532 /* UserMediaDisabled.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */; };
0799C34B1EBA3301003B7532 /* disableGetUserMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */; };
07C046CA1E4262A8007201E7 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C046C91E42573E007201E7 /* CARingBuffer.cpp */; };
@@ -1559,6 +1561,7 @@
AD57AC221DA7466E00FF1BDE /* many-iframes.html in Copy Resources */,
7772ECE122FE06C60009A799 /* many-same-origin-iframes.html in Copy Resources */,
CD8394DF232AF7C000149495 /* media-loading.html in Copy Resources */,
+ 0794742D25CB33FD00C597EB /* media-remote.html in Copy Resources */,
CDA3159A1ED548F1009F60D3 /* MediaPlaybackSleepAssertion.html in Copy Resources */,
CDC9442F1EF205D60059C3C4 /* mediastreamtrack-detached.html in Copy Resources */,
E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */,
@@ -1722,6 +1725,8 @@
07492B3A1DF8AE2D00633DE1 /* EnumerateMediaDevices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnumerateMediaDevices.cpp; sourceTree = "<group>"; };
0766DD1F1A5AD5200023E3BB /* PendingAPIRequestURL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PendingAPIRequestURL.cpp; sourceTree = "<group>"; };
076E507E1F45031E006E9F5A /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = "<group>"; };
+ 0794740C25CA0BDE00C597EB /* MediaSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaSession.mm; sourceTree = "<group>"; };
+ 0794742C25CB33B000C597EB /* media-remote.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "media-remote.html"; sourceTree = "<group>"; };
0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = disableGetUserMedia.html; sourceTree = "<group>"; };
07C046C91E42573E007201E7 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CARingBuffer.cpp; sourceTree = "<group>"; };
07CC7DFD2266330800E39181 /* MediaBufferingPolicy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaBufferingPolicy.mm; sourceTree = "<group>"; };
@@ -3359,6 +3364,7 @@
95B6B3B6251EBF2F00FC4382 /* MediaDocument.mm */,
CD0370E224A44B7A00BA3CAE /* MediaLoading.mm */,
07EF76D42540FC060053ED53 /* MediaMutedState.mm */,
+ 0794740C25CA0BDE00C597EB /* MediaSession.mm */,
51BE9E652376089500B4E117 /* MediaType.mm */,
5165FE03201EE617009F7EC3 /* MessagePortProviders.mm */,
51CD1C6A1B38CE3600142CA5 /* ModalAlerts.mm */,
@@ -3922,6 +3928,7 @@
46C519E31D35629600DAA51A /* LocalStorageNullEntries.localstorage */,
46C519E41D35629600DAA51A /* LocalStorageNullEntries.localstorage-shm */,
7A6A2C711DCCFB0200C0D085 /* LocalStorageQuirkEnabled.html */,
+ 0794742C25CB33B000C597EB /* media-remote.html */,
9B59F12920340854009E63D5 /* mso-list-compat-mode.html */,
9BCD4119206D5ED7001D71BE /* mso-list-on-h4.html */,
9BF356CC202D44F200F71160 /* mso-list.html */,
@@ -5383,6 +5390,7 @@
CD0370E324A44D9600BA3CAE /* MediaLoading.mm in Sources */,
07EF76D52540FC060053ED53 /* MediaMutedState.mm in Sources */,
CDA315981ED53651009F60D3 /* MediaPlaybackSleepAssertion.mm in Sources */,
+ 0794740D25CA0BDE00C597EB /* MediaSession.mm in Sources */,
CDC9442E1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm in Sources */,
51BE9E662376089F00B4E117 /* MediaType.mm in Sources */,
7CCE7EC51A411A7E00447C4C /* MemoryCacheDisableWithinResourceLoadDelegate.mm in Sources */,
@@ -5421,6 +5429,7 @@
C104BC1F2547237100C078C9 /* OverrideAppleLanguagesPreference.mm in Sources */,
CEBCA12F1E3A660100C73293 /* OverrideContentSecurityPolicy.mm in Sources */,
2DA2586F225C67DC00B45C1C /* OverrideViewportArguments.mm in Sources */,
+ 953ABB3525C0D682004C8B73 /* PageExtendedBackgroundColor.mm in Sources */,
7CCB4DA91C83AE7300CC6918 /* PageGroup.cpp in Sources */,
7CCE7F071A411AE600447C4C /* PageLoadBasic.cpp in Sources */,
7CCE7F081A411AE600447C4C /* PageLoadDidChangeLocationWithinPageForFrame.cpp in Sources */,
@@ -5464,7 +5473,6 @@
518C1153205B0504001FF4AE /* ProcessSwapOnNavigation.mm in Sources */,
FEC2A85624CEB65F00ADBC35 /* PropertySlot.cpp in Sources */,
7C83E0C11D0A652F00FEBCF3 /* ProvisionalURLNotChange.mm in Sources */,
- 953ABB3525C0D682004C8B73 /* PageExtendedBackgroundColor.mm in Sources */,
5CFACF65226FD2DC0056C7D0 /* Proxy.mm in Sources */,
041A1E34216FFDBC00789E0A /* PublicSuffix.cpp in Sources */,
7C83E0C21D0A653500FEBCF3 /* QuickLook.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaSession.mm (0 => 272445)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaSession.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaSession.mm 2021-02-05 23:18:55 UTC (rev 272445)
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if PLATFORM(MAC) && ENABLE(MEDIA_SESSION)
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/WebViewPrivate.h>
+#import <pal/spi/mac/MediaRemoteSPI.h>
+#import <wtf/Function.h>
+#import <wtf/HashSet.h>
+#import <wtf/NeverDestroyed.h>
+#import <wtf/SoftLinking.h>
+#import <wtf/text/StringHash.h>
+#import <wtf/text/WTFString.h>
+
+SOFT_LINK_PRIVATE_FRAMEWORK(MediaRemote)
+
+SOFT_LINK(MediaRemote, MRMediaRemoteSendCommandToApp, Boolean, (MRMediaRemoteCommand command, CFDictionaryRef options, MROriginRef origin, CFStringRef appDisplayID, MRSendCommandAppOptions appOptions, dispatch_queue_t replyQ, void(^completion)(MRSendCommandError err, CFArrayRef handlerReturnStatuses)), (command, options, origin, appDisplayID, appOptions, replyQ, completion));
+#define MRMediaRemoteSendCommandToApp softLinkMRMediaRemoteSendCommandToApp
+
+SOFT_LINK(MediaRemote, MRMediaRemoteGetLocalOrigin, MROriginRef, (), ());
+#define MRMediaRemoteGetLocalOrigin softLinkMRMediaRemoteGetLocalOrigin
+
+SOFT_LINK(MediaRemote, MRMediaRemoteGetSupportedCommandsForOrigin, void, (MROriginRef origin, dispatch_queue_t queue, void(^completion)(CFArrayRef commands)), (origin, queue, completion));
+#define MRMediaRemoteGetSupportedCommandsForOrigin softLinkMRMediaRemoteGetSupportedCommandsForOrigin
+
+SOFT_LINK(MediaRemote, MRMediaRemoteGetNowPlayingClient, void, (dispatch_queue_t queue, void(^completion)(MRNowPlayingClientRef, CFErrorRef)), (queue, completion))
+#define MRMediaRemoteGetNowPlayingClient softLinkMRMediaRemoteGetNowPlayingClient
+
+SOFT_LINK(MediaRemote, MRNowPlayingClientGetProcessIdentifier, pid_t, (MRNowPlayingClientRef client), (client))
+#define MRNowPlayingClientGetProcessIdentifier softLinkMRNowPlayingClientGetProcessIdentifier
+
+SOFT_LINK_CONSTANT(MediaRemote, kMRMediaRemoteOptionSkipInterval, CFStringRef)
+#define kMRMediaRemoteOptionSkipInterval getkMRMediaRemoteOptionSkipInterval()
+
+SOFT_LINK_CONSTANT(MediaRemote, kMRMediaRemoteOptionPlaybackPosition, CFStringRef)
+#define kMRMediaRemoteOptionPlaybackPosition getkMRMediaRemoteOptionPlaybackPosition()
+
+#if !USE(APPLE_INTERNAL_SDK)
+@interface MRCommandInfo : NSObject
+@property (nonatomic, readonly) MRMediaRemoteCommand command;
+@property (nonatomic, readonly, getter=isEnabled) BOOL enabled;
+@property (nonatomic, readonly, nullable, copy) NSDictionary *options;
+@end
+#endif
+
+namespace TestWebKitAPI {
+
+class MediaSessionTest : public testing::Test {
+public:
+ void SetUp() final
+ {
+ _configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ [_configuration setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeAudio];
+
+ _webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:_configuration.get() addToWindow:YES]);
+
+ WKPreferences *preferences = [_webView configuration].preferences;
+ preferences._mediaSessionEnabled = YES;
+
+ _messageHandlers = adoptNS([[NSMutableArray alloc] init]);
+ }
+
+ void TearDown() override
+ {
+ [_webView clearMessageHandlers:_messageHandlers.get()];
+ }
+
+ TestWKWebView* webView() { return _webView.get(); }
+
+ pid_t webViewPid() { return [_webView _webProcessIdentifier]; }
+
+ RetainPtr<MRNowPlayingClientRef> getNowPlayingClient()
+ {
+ bool gotNowPlaying = false;
+ RetainPtr<MRNowPlayingClientRef> nowPlayingClient;
+ MRMediaRemoteGetNowPlayingClient(dispatch_get_main_queue(), [&] (MRNowPlayingClientRef player, CFErrorRef error) {
+ if (!error && player)
+ nowPlayingClient = player;
+ gotNowPlaying = true;
+ });
+ TestWebKitAPI::Util::run(&gotNowPlaying);
+ return nowPlayingClient;
+ }
+
+ pid_t getNowPlayingClientPid()
+ {
+ return MRNowPlayingClientGetProcessIdentifier(getNowPlayingClient().get());
+ }
+
+ void loadPageAndBecomeNowPlaying(const String& pageName)
+ {
+ [_webView synchronouslyLoadTestPageNamed:pageName];
+
+ bool canplaythrough = false;
+ [webView() performAfterReceivingMessage:@"canplaythrough event" action:[&] {
+ canplaythrough = true;
+ }];
+ runScriptWithUserGesture("load()");
+ Util::run(&canplaythrough);
+
+ play();
+ pause();
+ ASSERT_EQ(webViewPid(), getNowPlayingClientPid());
+ }
+
+ void runScriptWithUserGesture(const String& script)
+ {
+ bool complete = false;
+ [_webView evaluateJavaScript:script completionHandler:[&] (id, NSError *) { complete = true; }];
+ TestWebKitAPI::Util::run(&complete);
+ }
+
+ void play()
+ {
+ bool playing = false;
+ [_webView performAfterReceivingMessage:@"play event" action:[&] { playing = true; }];
+ runScriptWithUserGesture("audio.play()");
+ Util::run(&playing);
+ }
+
+ void pause()
+ {
+ bool paused = false;
+ [_webView performAfterReceivingMessage:@"pause event" action:[&] { paused = true; }];
+ runScriptWithUserGesture("audio.pause()");
+ Util::run(&paused);
+ }
+
+ bool sendMediaRemoteCommand(MRMediaRemoteCommand command, CFDictionaryRef options = nullptr)
+ {
+ bool completed = false;
+ bool success;
+
+ MRMediaRemoteSendCommandToApp(command, options, NULL, NULL, static_cast<MRSendCommandAppOptions>(0), NULL, [&] (MRSendCommandError error, CFArrayRef) {
+ success = !error;
+ completed = true;
+ });
+ TestWebKitAPI::Util::run(&completed);
+
+ return success;
+ }
+
+ bool sendMediaRemoteSeekCommand(MRMediaRemoteCommand command, double interval)
+ {
+ CFStringRef seekInterval = (command == MRMediaRemoteCommandSeekToPlaybackPosition) ? kMRMediaRemoteOptionPlaybackPosition : kMRMediaRemoteOptionSkipInterval;
+ NSDictionary *options = @{(__bridge NSString *)seekInterval : @(interval)};
+ return sendMediaRemoteCommand(command, (__bridge CFDictionaryRef)options);
+ }
+
+ void listenForEventMessages(std::initializer_list<const char*> events)
+ {
+ for (auto* event : events) {
+ auto eventMessage = makeString(event, " event");
+ [_messageHandlers addObject:eventMessage];
+ [webView() performAfterReceivingMessage:eventMessage action:[this, eventMessage = WTFMove(eventMessage)] {
+ _eventListenersCalled.add(eventMessage);
+ }];
+ }
+ }
+
+ bool eventListenerWasCalled(const String& event)
+ {
+ return _eventListenersCalled.contains(makeString(event, " event"));
+ }
+
+ void clearEventListenerState()
+ {
+ _eventListenersCalled.clear();
+ }
+
+ void waitForEventListenerToBeCalled(const String& event)
+ {
+ int tries = 0;
+ do {
+ if (eventListenerWasCalled(event))
+ return;
+ Util::sleep(0.1);
+ } while (++tries <= 50);
+
+ return;
+ }
+
+ void listenForSessionHandlerMessages(std::initializer_list<const char*> handlers)
+ {
+ for (auto* handler : handlers) {
+ auto handlerMessage = makeString(handler, " handler");
+ [_messageHandlers addObject:handlerMessage];
+ [webView() performAfterReceivingMessage:handlerMessage action:[this, handlerMessage = WTFMove(handlerMessage)] {
+ _mediaSessionHandlersCalled.add(handlerMessage);
+ }];
+ }
+ }
+
+ bool sessionHandlerWasCalled(const String& handler)
+ {
+ return _mediaSessionHandlersCalled.contains(makeString(handler, " handler"));
+ }
+
+ void waitForSessionHandlerToBeCalled(const String& handler)
+ {
+ int tries = 0;
+ do {
+ if (sessionHandlerWasCalled(handler))
+ return;
+ Util::sleep(0.1);
+ } while (++tries <= 50);
+
+ return;
+ }
+
+ RetainPtr<NSArray> getSupportedCommands()
+ {
+ bool completed = false;
+ RetainPtr<NSArray> result;
+
+ MRMediaRemoteGetSupportedCommandsForOrigin(MRMediaRemoteGetLocalOrigin(), dispatch_get_main_queue(), [&] (CFArrayRef commands) {
+ result = (__bridge NSArray *)commands;
+ completed = true;
+ });
+
+ TestWebKitAPI::Util::run(&completed);
+
+ return result;
+ }
+
+private:
+ RetainPtr<WKWebViewConfiguration> _configuration;
+ RetainPtr<TestWKWebView> _webView;
+
+ HashSet<String> _mediaSessionHandlersCalled;
+ HashSet<String> _eventListenersCalled;
+ RetainPtr<NSMutableArray> _messageHandlers;
+};
+
+TEST_F(MediaSessionTest, OnlyOneHandler)
+{
+ loadPageAndBecomeNowPlaying("media-remote");
+
+ [webView() objectByEvaluatingJavaScript:@"setEmptyActionHandlers([ 'play' ])"];
+
+ listenForSessionHandlerMessages({ "play", "pause", "seekto", "seekforward", "seekbackward", "previoustrack", "nexttrack" });
+ listenForEventMessages({ "play", "pause", "seeked" });
+
+ static Vector<MRMediaRemoteCommand> registeredCommands = { MRMediaRemoteCommandPlay };
+ auto currentCommands = getSupportedCommands();
+ for (MRCommandInfo *command in currentCommands.get()) {
+ if (!command.enabled)
+ continue;
+
+ ASSERT_TRUE(registeredCommands.contains(command.command));
+ }
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPlay));
+ waitForSessionHandlerToBeCalled("play");
+ ASSERT_TRUE(sessionHandlerWasCalled("play"));
+ ASSERT_FALSE(eventListenerWasCalled("play"));
+
+ // The media session only registered for Play, but no other commands should reach HTMLMediaElement.
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipForward, 1));
+ ASSERT_FALSE(sessionHandlerWasCalled("seekforward"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipBackward, 10));
+ ASSERT_FALSE(sessionHandlerWasCalled("seekbackward"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSeekToPlaybackPosition, 6));
+ ASSERT_FALSE(sessionHandlerWasCalled("seekto"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandNextTrack));
+ ASSERT_FALSE(sessionHandlerWasCalled("nexttrack"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPreviousTrack));
+ ASSERT_FALSE(sessionHandlerWasCalled("previoustrack"));
+}
+
+TEST_F(MediaSessionTest, RemoteCommands)
+{
+ loadPageAndBecomeNowPlaying("media-remote");
+
+ [webView() objectByEvaluatingJavaScript:@"setEmptyActionHandlers([ 'play', 'pause', 'seekto', 'seekforward', 'seekbackward', 'previoustrack', 'nexttrack' ])"];
+
+ listenForSessionHandlerMessages({ "play", "pause", "seekto", "seekforward", "seekbackward", "previoustrack", "nexttrack" });
+ listenForEventMessages({ "play", "pause", "seeked" });
+
+ static Vector<MRMediaRemoteCommand> registeredCommands = { MRMediaRemoteCommandPlay, MRMediaRemoteCommandPause, MRMediaRemoteCommandSeekToPlaybackPosition, MRMediaRemoteCommandSkipForward, MRMediaRemoteCommandSkipBackward, MRMediaRemoteCommandPreviousTrack, MRMediaRemoteCommandNextTrack };
+ auto currentCommands = getSupportedCommands();
+ for (MRCommandInfo *command in currentCommands.get()) {
+ if (!command.enabled)
+ continue;
+
+ ASSERT_TRUE(registeredCommands.contains(command.command));
+ }
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPlay));
+ waitForSessionHandlerToBeCalled("play");
+ ASSERT_TRUE(sessionHandlerWasCalled("play"));
+ ASSERT_FALSE(eventListenerWasCalled("play"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPause));
+ waitForSessionHandlerToBeCalled("pause");
+ ASSERT_TRUE(sessionHandlerWasCalled("pause"));
+ ASSERT_FALSE(eventListenerWasCalled("pause"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipForward, 1));
+ waitForSessionHandlerToBeCalled("seekforward");
+ ASSERT_TRUE(sessionHandlerWasCalled("seekforward"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipBackward, 10));
+ waitForSessionHandlerToBeCalled("seekbackward");
+ ASSERT_TRUE(sessionHandlerWasCalled("seekbackward"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSeekToPlaybackPosition, 6));
+ waitForSessionHandlerToBeCalled("seekto");
+ ASSERT_TRUE(sessionHandlerWasCalled("seekto"));
+ ASSERT_FALSE(eventListenerWasCalled("seeked"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandNextTrack));
+ waitForSessionHandlerToBeCalled("nexttrack");
+ ASSERT_TRUE(sessionHandlerWasCalled("nexttrack"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPreviousTrack));
+ waitForSessionHandlerToBeCalled("previoustrack");
+ ASSERT_TRUE(sessionHandlerWasCalled("previoustrack"));
+
+ // Unregister action handlers, supported commands should go to HTMLMediaElement.
+ [webView() objectByEvaluatingJavaScript:@"clearActionHandlers()"];
+ clearEventListenerState();
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPlay));
+ waitForEventListenerToBeCalled("play");
+ ASSERT_TRUE(eventListenerWasCalled("play"));
+
+ ASSERT_TRUE(sendMediaRemoteCommand(MRMediaRemoteCommandPause));
+ waitForEventListenerToBeCalled("pause");
+ ASSERT_TRUE(eventListenerWasCalled("pause"));
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipForward, 1));
+ waitForEventListenerToBeCalled("seeked");
+ ASSERT_TRUE(eventListenerWasCalled("seeked"));
+ clearEventListenerState();
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSkipBackward, 10));
+ waitForEventListenerToBeCalled("seeked");
+ ASSERT_TRUE(eventListenerWasCalled("seeked"));
+ clearEventListenerState();
+
+ ASSERT_TRUE(sendMediaRemoteSeekCommand(MRMediaRemoteCommandSeekToPlaybackPosition, 6));
+ waitForEventListenerToBeCalled("seeked");
+ ASSERT_TRUE(eventListenerWasCalled("seeked"));
+}
+
+}
+
+#endif // PLATFORM(MAC) && ENABLE(MEDIA_SESSION)
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/media-remote.html (0 => 272445)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/media-remote.html (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/media-remote.html 2021-02-05 23:18:55 UTC (rev 272445)
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ #eventslog {
+ font-size: 10px;
+ height: calc(100% - (320px));
+ overflow-y: scroll;
+ border: 1px solid rgba(147, 128, 108, 0.25);
+ }
+</style>
+ <script>
+
+ let playlist = {
+ current: 0,
+ metadata: [
+ {
+ url: 'video-with-audio.mp4',
+ title: 'Bip Bop With Audio',
+ artist: 'The Bip Bop Authors',
+ album: 'WebKit Greatest Hits',
+ artwork: [
+
+ ],
+ },
+ {
+ url: 'video-without-audio.mp4',
+ title: 'Bip Bop Without Audio',
+ artist: 'The Bip Bop Authors',
+ album: 'WebKit Original Test Content',
+ artwork: [
+
+ ],
+ },
+ ],
+ };
+
+ function setMetadata(data)
+ {
+ navigator.mediaSession.metadata = new MediaMetadata(data);
+ postMessage('set metadata')
+ }
+
+ function setPlaylistMetadata(index)
+ {
+ setMetadata(playlist.metadata[index]);
+
+ }
+
+ function clearActionHandlers()
+ {
+ setEmptyActionHandlers([]);
+ }
+
+ function setEmptyActionHandlers(handlers)
+ {
+ const actions = {
+ 'play' : () => { postMessage('play handler'); },
+
+ 'pause' : () => { postMessage('pause handler') },
+
+ 'seekto' : (details) => { postMessage('seekto handler') },
+
+ 'seekforward' : (details) => { postMessage('seekforward handler') },
+
+ 'seekbackward' : (details) => { postMessage('seekbackward handler') },
+
+ 'previoustrack' : () => { postMessage('previoustrack handler') },
+
+ 'nexttrack' : () => { postMessage('nexttrack handler') },
+ };
+
+ Object.keys(actions).forEach(action ="" { navigator.mediaSession.setActionHandler(action, null); });
+
+ handlers.forEach(handler => {
+ if (!actions[handler]) {
+ log(`asked to register handler for unknown action '${handler}'`);
+ return;
+ }
+
+ log(`registering '${handler}' handler`);
+ navigator.mediaSession.setActionHandler(handler, actions[handler]);
+ });
+ }
+
+ function postMessage(message)
+ {
+ log(`${message}`)
+ if (window.webkit)
+ window.webkit.messageHandlers.testHandler.postMessage(message);
+ }
+
+ function postEvent(evt)
+ {
+ postMessage(`${evt.type} event`);
+ }
+
+ function load()
+ {
+ let src = ""
+ if (!window.webkit)
+ src = ""
+ audio.src = ""
+ audio.load();
+ }
+
+ function log(msg)
+ {
+ let eventLog = document.getElementById('eventslog')
+ let note = document.createElement('div');
+ note.innerHTML = msg;
+ eventLog.insertBefore(note, eventLog.firstChild);
+ }
+
+
+ window.addEventListener("load", evt => {
+ audio = document.getElementsByTagName('audio')[0];
+ audio.addEventListener('canplaythrough', postEvent);
+ audio.addEventListener('play', postEvent);
+ audio.addEventListener('pause', postEvent);
+ audio.addEventListener('seeked', postEvent);
+ }, false);
+
+ </script>
+</head>
+<body>
+ <audio controls> </audio>
+ <br>
+ <button _onclick_="setEmptyActionHandlers(['play','pause','seekto'])">Empty Play, Pause, SeekTo</button>
+ <br>
+ <button _onclick_="setEmptyActionHandlers(['play','pause','seekforward','seekbackward'])">Empty Play, Pause, Seek Forward, Seek Backward</button>
+ <br>
+ <button _onclick_="load()">Load</button>
+ <br>
+ <button _onclick_="setPlaylistMetadata(0)">Set Metadata 1</button><button _onclick_="setPlaylistMetadata(1)">Set Metadata 2</button>
+ <br>
+ <div id='eventslog'></div>
+</body>
+</html>