Title: [275314] trunk
Revision
275314
Author
eric.carl...@apple.com
Date
2021-03-31 16:11:14 -0700 (Wed, 31 Mar 2021)

Log Message

[macOS] MediaSessionCoordinator should have join and leave methods
https://bugs.webkit.org/show_bug.cgi?id=223955
<rdar://problem/76021588>

Reviewed by Jer Noble.
Source/WebCore:

Add 'join' and 'leave' methods to MediaSessionCoordinator so a page has to opt-in
to participating in a coordinated session and can leave at any time. Don't have
the coordinator automatically call session methods when the private coordinator
finishes, just signal the promise and let the page handle it.

No new tests, updated media/media-session/mock-coordinator.html.

* CMakeLists.txt:
* DerivedSources-input.xcfilelist:
* DerivedSources-output.xcfilelist:
* DerivedSources.make:
* Modules/mediasession/MediaMetadata.idl: Fix Conditional.
* Modules/mediasession/MediaMetadataPlaylistMixin.idl: Ditto.

* Modules/mediasession/MediaPositionState.h: Add logging template.
(WTF::LogArgument<WebCore::MediaPositionState>::toString):

* Modules/mediasession/MediaSession.cpp:
(WebCore::MediaSession::MediaSession): Switch from beging a ContextDestructionObserver
to an ActiveDOMObject to the wrapper won't be collected while an event dispatch
is pending.
(WebCore::MediaSession::virtualHasPendingActivity const): Prevent collection while
event dispatch is pending.
(WebCore::MediaSession::setPositionState): Improve logging.
(WebCore::MediaPositionState::toJSONString const):
* Modules/mediasession/MediaSession.h:
* Modules/mediasession/MediaSession.idl:

* Modules/mediasession/MediaSessionCoordinator.cpp:
(WebCore::MediaSessionCoordinator::join): New.
(WebCore::MediaSessionCoordinator::leave): New.
(WebCore::MediaSessionCoordinator::seekTo): Reject unless state is 'joined'. Don't
call session method.
(WebCore::MediaSessionCoordinator::play): Ditto.
(WebCore::MediaSessionCoordinator::pause): Ditto.
(WebCore::MediaSessionCoordinator::setTrack): Ditto.
(WebCore::MediaSessionCoordinator::positionStateChanged):
(WebCore::MediaSessionCoordinator::playbackStateChanged):
(WebCore::MediaSessionCoordinator::readyStateChanged): Do nothing unless state is
'joined'. Improve logging.
(WebCore::MediaSessionCoordinator::seekSessionToTime): Ditto.
(WebCore::MediaSessionCoordinator::playSession): Ditto.
(WebCore::MediaSessionCoordinator::pauseSession): Ditto.
(WebCore::MediaSessionCoordinator::setSessionTrack): Ditto.
* Modules/mediasession/MediaSessionCoordinator.h:
(WebCore::MediaSessionCoordinator::identifier const):
(WebCore::MediaSessionCoordinator::state const):
* Modules/mediasession/MediaSessionCoordinator.idl:
* Modules/mediasession/MediaSessionCoordinatorMixin.idl: Fix Conditional.

* Modules/mediasession/MediaSessionCoordinatorPrivate.h: Declare new required methods.

* Modules/mediasession/MediaSessionCoordinatorState.h: Define states.
* Modules/mediasession/MediaSessionCoordinatorState.idl:

* Modules/mediasession/MediaSessionPlaylistMixin.idl: Fix Conditional.

* Sources.txt: Add JSMediaSessionCoordinatorState.cpp.

* WebCore.xcodeproj/project.pbxproj:

* testing/MockMediaSessionCoordinator.cpp:
(WebCore::MockMediaSessionCoordinator::join):
(WebCore::MockMediaSessionCoordinator::leave):
(WebCore::MockMediaSessionCoordinator::coordinatorStateChanged):
* testing/MockMediaSessionCoordinator.h:

Source/WebKit:

* UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h: Renamed from Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h.
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp:
(WebKit::RemoteMediaSessionCoordinatorProxy::create):
(WebKit::RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy):
(WebKit::RemoteMediaSessionCoordinatorProxy::join):
(WebKit::RemoteMediaSessionCoordinatorProxy::leave):
(WebKit::RemoteMediaSessionCoordinatorProxy::coordinatorStateChanged):
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h:
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::createMediaSessionCoordinator):
* UIProcess/WebPageProxy.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp:
(WebKit::RemoteMediaSessionCoordinator::create):
(WebKit::RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator):
(WebKit::RemoteMediaSessionCoordinator::join):
(WebKit::RemoteMediaSessionCoordinator::leave):
(WebKit::RemoteMediaSessionCoordinator::coordinatorStateChanged):
* WebProcess/MediaSession/RemoteMediaSessionCoordinator.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::createMediaSessionCoordinator):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

* UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h: Renamed from Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h.
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp:
(WebKit::RemoteMediaSessionCoordinatorProxy::create):
(WebKit::RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy):
(WebKit::RemoteMediaSessionCoordinatorProxy::coordinatorStateChanged):
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h:
* UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::createMediaSessionCoordinator):
* UIProcess/WebPageProxy.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp:
(WebKit::RemoteMediaSessionCoordinator::create):
(WebKit::RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator):
(WebKit::RemoteMediaSessionCoordinator::coordinatorStateChanged):
* WebProcess/MediaSession/RemoteMediaSessionCoordinator.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::createMediaSessionCoordinator):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

LayoutTests:

* media/media-session/mock-coordinator-expected.txt:
* media/media-session/mock-coordinator.html:

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (275313 => 275314)


--- trunk/LayoutTests/ChangeLog	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/LayoutTests/ChangeLog	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1,3 +1,14 @@
+2021-03-31  Eric Carlson  <eric.carl...@apple.com>
+
+        [macOS] MediaSessionCoordinator should have join and leave methods
+        https://bugs.webkit.org/show_bug.cgi?id=223955
+        <rdar://problem/76021588>
+
+        Reviewed by Jer Noble.
+
+        * media/media-session/mock-coordinator-expected.txt:
+        * media/media-session/mock-coordinator.html:
+
 2021-03-31  Robert Jenner  <jen...@apple.com>
 
         REGRESSION (r275227): [ macOS iOS Release ] imported/w3c/web-platform-tests/css/css-transitions/properties-value-003.html is a constant failure

Modified: trunk/LayoutTests/media/media-session/mock-coordinator-expected.txt (275313 => 275314)


--- trunk/LayoutTests/media/media-session/mock-coordinator-expected.txt	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/LayoutTests/media/media-session/mock-coordinator-expected.txt	2021-03-31 23:11:14 UTC (rev 275314)
@@ -7,28 +7,100 @@
 EVENT(coordinatorchange)
 EXPECTED (navigator.mediaSession.coordinator != 'undefined') OK
 
+** navigator.mediaSession.coordinator.state should be "waiting" intitially
+EXPECTED (navigator.mediaSession.coordinator.state == 'waiting') OK
+
+** Test that when coordinator methods fail and promises reject before mediaSession.join() is called.
+RUN(promise = navigator.mediaSession.coordinator.play())
+Promise rejected correctly OK
+
+RUN(promise = navigator.mediaSession.coordinator.pause())
+Promise rejected correctly OK
+
+RUN(promise = navigator.mediaSession.coordinator.seekTo(10))
+Promise rejected correctly OK
+
+** Test that mediaSession does not notify coordinator when states change before mediaSession.join() is called.
+* PositionState
+RUN(navigator.mediaSession.setPositionState({ duration: 1, playbackRate: 1, position: 0 }))
+EXPECTED (latestChange == '') OK
+
+* ReadyState
+EXPECTED (navigator.mediaSession.readyState == 'haveNothing') OK
+RUN(navigator.mediaSession.readyState = 'haveMetadata')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveMetadata') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveMetadata') OK
+RUN(navigator.mediaSession.readyState = 'haveCurrentData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveCurrentData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveCurrentData') OK
+RUN(navigator.mediaSession.readyState = 'haveFutureData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveFutureData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveFutureData') OK
+RUN(navigator.mediaSession.readyState = 'haveEnoughData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveEnoughData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveEnoughData') OK
+RUN(navigator.mediaSession.readyState = 'haveNothing')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveNothing') OK
+
+* PlaybackState
+EXPECTED (navigator.mediaSession.playbackState == 'none') OK
+RUN(navigator.mediaSession.playbackState = 'paused')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
+
+EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
+RUN(navigator.mediaSession.playbackState = 'playing')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
+
+EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
+RUN(navigator.mediaSession.playbackState = 'none')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'none') OK
+
+
+
+** session.join() should reject on failure
+RUN(internals.setMockMediaSessionCoordinatorCommandsShouldFail(true))
+RUN(promise = navigator.mediaSession.coordinator.join())
+Promise rejected correctly OK
+EXPECTED (navigator.mediaSession.coordinator.state == 'waiting') OK
+EXPECTED (latestChange == '') OK
+
+RUN(internals.setMockMediaSessionCoordinatorCommandsShouldFail(false))
+RUN(promise = navigator.mediaSession.coordinator.join())
+Promise resolved OK
+EXPECTED (navigator.mediaSession.coordinator.state == 'joined') OK
+EXPECTED (latestChange == 'coordinatorStateChanged') OK
+
 ** Test that when coordinator methods succeed, promises resolve and mediaSession action handlers are called.
-Test navigator.mediaSession.coordinator.play()
-ACTION(play)
-EXPECTED (latestAction == 'play') OK
+RUN(promise = navigator.mediaSession.coordinator.play())
+Promise resolved OK
 
-Test navigator.mediaSession.coordinator.pause()
-ACTION(pause)
-EXPECTED (latestAction == 'pause') OK
+RUN(promise = navigator.mediaSession.coordinator.pause())
+Promise resolved OK
 
-Test navigator.mediaSession.coordinator.seekto()
-ACTION(seekto)
-EXPECTED (latestAction == 'seekto') OK
+RUN(promise = navigator.mediaSession.coordinator.seekTo(10))
+Promise resolved OK
 
-** Test that when coordinator methods succeed, promises reject and mediaSession action handlers are not called.
-Test navigator.mediaSession.coordinator.play()
-EXPECTED (latestAction == '') OK
+** Test that when coordinator methods fail, promises reject and mediaSession action handlers are not called.
+RUN(promise = navigator.mediaSession.coordinator.play())
+Promise rejected correctly OK
 
-Test navigator.mediaSession.coordinator.pause()
-EXPECTED (latestAction == '') OK
+RUN(promise = navigator.mediaSession.coordinator.pause())
+Promise rejected correctly OK
 
-Test navigator.mediaSession.coordinator.seekto()
-EXPECTED (latestAction == '') OK
+RUN(promise = navigator.mediaSession.coordinator.seekTo(10))
+Promise rejected correctly OK
 
 ** Test that mediaSession notifies coordinator when positionState changes.
 RUN(navigator.mediaSession.setPositionState({ duration: 1, playbackRate: 1, position: 0 }))
@@ -63,19 +135,86 @@
 ** Test that mediaSession notifies coordinator when playbackState changes.
 EXPECTED (navigator.mediaSession.playbackState == 'none') OK
 RUN(navigator.mediaSession.playbackState = 'paused')
-EXPECTED (latestChange == 'playbackStateChanged') OK
 EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
 
 EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
 RUN(navigator.mediaSession.playbackState = 'playing')
-EXPECTED (latestChange == 'playbackStateChanged') OK
 EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
 
 EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
 RUN(navigator.mediaSession.playbackState = 'none')
-EXPECTED (latestChange == 'playbackStateChanged') OK
 EXPECTED (navigator.mediaSession.playbackState == 'none') OK
 
 
+
+** Leave the session
+RUN(navigator.mediaSession.coordinator.leave())
+EXPECTED (navigator.mediaSession.coordinator.state == 'closed') OK
+EXPECTED (latestChange == 'coordinatorStateChanged') OK
+
+** Test that when coordinator methods fail and promises reject after mediaSession.leave() has been called.
+RUN(promise = navigator.mediaSession.coordinator.play())
+Promise rejected correctly OK
+
+RUN(promise = navigator.mediaSession.coordinator.pause())
+Promise rejected correctly OK
+
+RUN(promise = navigator.mediaSession.coordinator.seekTo(10))
+Promise rejected correctly OK
+
+** Test that mediaSession does not notify coordinator when states change after mediaSession.leave() has been called.
+* PositionState
+RUN(navigator.mediaSession.setPositionState({ duration: 1, playbackRate: 1, position: 0 }))
+EXPECTED (latestChange == '') OK
+
+* ReadyState
+EXPECTED (navigator.mediaSession.readyState == 'haveNothing') OK
+RUN(navigator.mediaSession.readyState = 'haveMetadata')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveMetadata') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveMetadata') OK
+RUN(navigator.mediaSession.readyState = 'haveCurrentData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveCurrentData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveCurrentData') OK
+RUN(navigator.mediaSession.readyState = 'haveFutureData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveFutureData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveFutureData') OK
+RUN(navigator.mediaSession.readyState = 'haveEnoughData')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveEnoughData') OK
+
+EXPECTED (navigator.mediaSession.readyState == 'haveEnoughData') OK
+RUN(navigator.mediaSession.readyState = 'haveNothing')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.readyState == 'haveNothing') OK
+
+* PlaybackState
+EXPECTED (navigator.mediaSession.playbackState == 'none') OK
+RUN(navigator.mediaSession.playbackState = 'paused')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
+
+EXPECTED (navigator.mediaSession.playbackState == 'paused') OK
+RUN(navigator.mediaSession.playbackState = 'playing')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
+
+EXPECTED (navigator.mediaSession.playbackState == 'playing') OK
+RUN(navigator.mediaSession.playbackState = 'none')
+EXPECTED (latestChange == '') OK
+EXPECTED (navigator.mediaSession.playbackState == 'none') OK
+
+
+
+** It should not be possible to join or leave a closed session
+RUN(promise = navigator.mediaSession.coordinator.join())
+Promise rejected correctly OK
+TEST(navigator.mediaSession.coordinator.leave()) THROWS(InvalidStateError: Unable to leave when state is closed) OK
+
 END OF TEST
 

Modified: trunk/LayoutTests/media/media-session/mock-coordinator.html (275313 => 275314)


--- trunk/LayoutTests/media/media-session/mock-coordinator.html	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/LayoutTests/media/media-session/mock-coordinator.html	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1,3 +1,4 @@
+<!-- webkit-test-runner [ MediaSessionCoordinatorEnabled=true ] -->
 <!DOCTYPE html>
 <html>
 <head>
@@ -5,6 +6,16 @@
     <script src=""
     <script>
 
+    let waiting = false;
+    async function waitForDebugger()
+    {
+        let counter = 0;
+        while (waiting) {
+            await sleepFor(100);
+            counter++;
+        }
+    }
+
     function changeHandler(change)
     {
         window.latestChange = change;
@@ -20,13 +31,17 @@
             return;
         }
 
-        const changePromise = new Promise(resolve => {
-            navigator.mediaSession.addEventListener('coordinatorchange', (event) => {
-                consoleWrite(`EVENT(${event.type})`);
-                resolve();
+        let changePromise = () => {
+            return new Promise(resolve => {
+                navigator.mediaSession.addEventListener('coordinatorchange', (event) => {
+                    consoleWrite(`EVENT(${event.type})`);
+                    resolve();
+                });
             });
-        });
+        }
 
+        await waitForDebugger();
+
         consoleWrite('');
         consoleWrite('** There should be no mediaSession.coordinator initially.')
 
@@ -37,7 +52,7 @@
 
         run('internals.registerMockMediaSessionCoordinator(changeHandler)');
 
-        await changePromise;
+        await changePromise();
         
         testExpected('navigator.mediaSession.coordinator', undefined, '!=');
 
@@ -48,38 +63,113 @@
             });
         });
 
-        let testAction = async (action, func, forceFailure) => {
+        let testAction = async (action, func, expectFailure) => {
             latestAction = '';
 
-            let succeeded;
+            run(`promise = ${func.toString()}`);
             try {
-                consoleWrite(`Test navigator.mediaSession.coordinator.${action}()`);
-                await func();
-                if (forceFailure)
-                    consoleWrite(`FAIL: ${func.toString()} resolved but should have rejected`);
-                testExpected('latestAction', action);
-            } catch (ex) {
-                if (!forceFailure)
-                    consoleWrite(`FAIL: ${func.toString()} threw ${ex}`);
-                testExpected('latestAction', '');
-            }
+                if (expectFailure)
+                    await shouldReject(promise);
+                else
+                    await shouldResolve(promise);
+            } catch(ex) {}
 
             consoleWrite('');
         };
 
+        let testReadyStates = async (testCallback) => {
+            let previousState = 'haveNothing';
+            navigator.mediaSession.readyState = 'haveNothing';
+            for (let state of ['haveMetadata', 'haveCurrentData', 'haveFutureData', 'haveEnoughData', 'haveNothing']) {
+                testExpected('navigator.mediaSession.readyState', previousState);
+
+                latestChange = '';
+                run(`navigator.mediaSession.readyState = '${state}'`);
+
+                await testCallback()
+
+                testExpected('navigator.mediaSession.readyState', state);
+                previousState = state;
+                consoleWrite('');
+            };
+        }
+
+        let testPlaybackStates = async (testCallback) => {
+            let previousState = 'none';
+            navigator.mediaSession.playbackState = 'none';
+            for (let state of ['paused', 'playing', 'none']) {
+
+                testExpected('navigator.mediaSession.playbackState', previousState);
+
+                latestChange = '';
+                run(`navigator.mediaSession.playbackState = '${state}'`);
+
+                await testCallback()
+
+                testExpected('navigator.mediaSession.playbackState', state);
+                previousState = state;
+                consoleWrite('');
+            };
+        }
+        
         let actions = [ 
-            { action: 'play', func : () => { return navigator.mediaSession.coordinator.play() }, },
-            { action: 'pause', func : () => { return navigator.mediaSession.coordinator.pause() }, },
-            { action: 'seekto', func : () => { return navigator.mediaSession.coordinator.seekTo(10) }, },
+            { action: 'play', func : 'navigator.mediaSession.coordinator.play()' },
+            { action: 'pause', func : 'navigator.mediaSession.coordinator.pause()' },
+            { action: 'seekto', func : 'navigator.mediaSession.coordinator.seekTo(10)' },
         ];
 
+        // Before joining
         consoleWrite('');
-        consoleWrite('** Test that when coordinator methods succeed,  promises resolve and mediaSession action handlers are called.')
+        consoleWrite('** navigator.mediaSession.coordinator.state should be "waiting" intitially')
+        testExpected('navigator.mediaSession.coordinator.state', 'waiting');
+
+        consoleWrite('');
+        consoleWrite('** Test that when coordinator methods fail and promises reject before mediaSession.join() is called.')
+        for (let action of actions)
+            await testAction(action.action, action.func, true);
+
+        consoleWrite(`** Test that mediaSession does not notify coordinator when states change before mediaSession.join() is called.`)
+        consoleWrite('* PositionState');
+        latestChange = '';
+        run('navigator.mediaSession.setPositionState({ duration: 1, playbackRate: 1, position: 0 })');
+        await sleepFor(20);
+        testExpected('latestChange', '');
+
+        consoleWrite('');
+        consoleWrite('* ReadyState');
+        await testReadyStates(async () => {
+            await sleepFor(20);
+            testExpected('latestChange', '');
+        });
+
+        consoleWrite('* PlaybackState');
+        await testPlaybackStates(async () => {
+            await sleepFor(20);
+            testExpected('latestChange', '');
+        });
+
+        consoleWrite('<br>');
+        consoleWrite('** session.join() should reject on failure');
+        run('internals.setMockMediaSessionCoordinatorCommandsShouldFail(true)');
+        run('promise = navigator.mediaSession.coordinator.join()');
+        await shouldReject(promise).then(() => { }).catch(() => { });
+        testExpected('navigator.mediaSession.coordinator.state', 'waiting');
+        testExpected('latestChange', '');
+
+        consoleWrite('');
+        run('internals.setMockMediaSessionCoordinatorCommandsShouldFail(false)');
+        run('promise = navigator.mediaSession.coordinator.join()');
+        await shouldResolve(promise).then(() => { }).catch(() => { });
+        testExpected('navigator.mediaSession.coordinator.state', 'joined');
+        testExpected('latestChange', 'coordinatorStateChanged');
+
+        consoleWrite('');
+        consoleWrite('** Test that when coordinator methods succeed, promises resolve and mediaSession action handlers are called.')
         internals.setMockMediaSessionCoordinatorCommandsShouldFail(false);
         for (let action of actions)
             await testAction(action.action, action.func, false);
 
-        consoleWrite('** Test that when coordinator methods succeed,  promises reject and mediaSession action handlers are not called.')
+        consoleWrite('** Test that when coordinator methods fail, promises reject and mediaSession action handlers are not called.')
         internals.setMockMediaSessionCoordinatorCommandsShouldFail(true);
         for (let action of actions)
             await testAction(action.action, action.func, true);
@@ -89,37 +179,59 @@
         await testExpectedEventually('latestChange', 'positionStateChanged', '==', 100);
 
         consoleWrite('');
-        consoleWrite('** Test that mediaSession notifies coordinator when readyState changes.')
-        let previousState = 'haveNothing';
-        for (let state of ['haveMetadata', 'haveCurrentData', 'haveFutureData', 'haveEnoughData', 'haveNothing']) {
-            testExpected('navigator.mediaSession.readyState', previousState);
+        consoleWrite('** Test that mediaSession notifies coordinator when readyState changes.');
+        await testReadyStates(async () => { await testExpectedEventually('latestChange', 'readyStateChanged', '==', 100); })
 
-            latestChange = '';
-            run(`navigator.mediaSession.readyState = '${state}'`);
+        consoleWrite('** Test that mediaSession notifies coordinator when playbackState changes.')
+        await testPlaybackStates(async () => { testExpectedEventually('latestChange', 'playbackStateChanged', '==', 100); });
 
-            await testExpectedEventually('latestChange', 'readyStateChanged', '==', 100);
+        // After leaving
+        consoleWrite('<br>');
+        consoleWrite('** Leave the session')
+        run('navigator.mediaSession.coordinator.leave()');
+        testExpected('navigator.mediaSession.coordinator.state', 'closed');
+        await testExpectedEventually('latestChange', 'coordinatorStateChanged', '==', 100);
+        latestChange = '';
 
-            testExpected('navigator.mediaSession.readyState', state);
-            previousState = state;
-            consoleWrite('');
-        };
+        consoleWrite('');
+        consoleWrite('** Test that when coordinator methods fail and promises reject after mediaSession.leave() has been called.')
+        for (let action of actions)
+            await testAction(action.action, action.func, true);
 
-        consoleWrite('** Test that mediaSession notifies coordinator when playbackState changes.')
-        previousState = 'none';
-        for (let state of ['paused', 'playing', 'none']) {
+        consoleWrite(`** Test that mediaSession does not notify coordinator when states change after mediaSession.leave() has been called.`)
+        consoleWrite('* PositionState');
+        latestChange = '';
+        run('navigator.mediaSession.setPositionState({ duration: 1, playbackRate: 1, position: 0 })');
+        await sleepFor(20);
+        testExpected('latestChange', '');
 
-            testExpected('navigator.mediaSession.playbackState', previousState);
+        consoleWrite('');
+        consoleWrite('* ReadyState');
+        await testReadyStates(async () => {
+            await sleepFor(20);
+            testExpected('latestChange', '');
+        });
 
-            latestChange = '';
-            run(`navigator.mediaSession.playbackState = '${state}'`);
+        consoleWrite('* PlaybackState');
+        await testPlaybackStates(async () => {
+            await sleepFor(20);
+            testExpected('latestChange', '');
+        });
 
-            await testExpectedEventually('latestChange', 'playbackStateChanged', '==', 100);
+        consoleWrite('<br>');
+        consoleWrite('** It should not be possible to join or leave a closed session');
+        run('promise = navigator.mediaSession.coordinator.join()');
+        await shouldReject(promise).then(() => { }).catch(() => { });
+        
+        let exception;
+        try {
+            navigator.mediaSession.coordinator.leave();
+        } catch (ex) {
+            exception = ex;
+        }
+        let expected = 'InvalidStateError: Unable to leave when state is closed';
+        logResult(exception.toString() == expected, `TEST(navigator.mediaSession.coordinator.leave()) THROWS(${expected})`);
 
-            testExpected('navigator.mediaSession.playbackState', state);
-            previousState = state;
-            consoleWrite('');
-        };
-
         consoleWrite('');
         endTest();
     }, {once: true});

Modified: trunk/Source/WebCore/CMakeLists.txt (275313 => 275314)


--- trunk/Source/WebCore/CMakeLists.txt	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/CMakeLists.txt	2021-03-31 23:11:14 UTC (rev 275314)
@@ -338,6 +338,7 @@
     Modules/mediasession/MediaSessionActionHandler.idl
     Modules/mediasession/MediaSessionCoordinator.idl
     Modules/mediasession/MediaSessionCoordinatorMixin.idl
+    Modules/mediasession/MediaSessionCoordinatorState.idl
     Modules/mediasession/MediaSessionPlaybackState.idl
     Modules/mediasession/MediaSessionPlaylistMixin.idl
     Modules/mediasession/MediaSessionReadyState.idl

Modified: trunk/Source/WebCore/ChangeLog (275313 => 275314)


--- trunk/Source/WebCore/ChangeLog	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/ChangeLog	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1,3 +1,78 @@
+2021-03-31  Eric Carlson  <eric.carl...@apple.com>
+
+        [macOS] MediaSessionCoordinator should have join and leave methods
+        https://bugs.webkit.org/show_bug.cgi?id=223955
+        <rdar://problem/76021588>
+
+        Reviewed by Jer Noble.
+        
+        Add 'join' and 'leave' methods to MediaSessionCoordinator so a page has to opt-in
+        to participating in a coordinated session and can leave at any time. Don't have 
+        the coordinator automatically call session methods when the private coordinator
+        finishes, just signal the promise and let the page handle it.
+
+        No new tests, updated media/media-session/mock-coordinator.html.
+
+        * CMakeLists.txt:
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources-output.xcfilelist:
+        * DerivedSources.make:
+        * Modules/mediasession/MediaMetadata.idl: Fix Conditional.
+        * Modules/mediasession/MediaMetadataPlaylistMixin.idl: Ditto.
+
+        * Modules/mediasession/MediaPositionState.h: Add logging template.
+        (WTF::LogArgument<WebCore::MediaPositionState>::toString):
+
+        * Modules/mediasession/MediaSession.cpp:
+        (WebCore::MediaSession::MediaSession): Switch from beging a ContextDestructionObserver
+        to an ActiveDOMObject to the wrapper won't be collected while an event dispatch
+        is pending.
+        (WebCore::MediaSession::virtualHasPendingActivity const): Prevent collection while
+        event dispatch is pending.
+        (WebCore::MediaSession::setPositionState): Improve logging.
+        (WebCore::MediaPositionState::toJSONString const):
+        * Modules/mediasession/MediaSession.h:
+        * Modules/mediasession/MediaSession.idl:
+
+        * Modules/mediasession/MediaSessionCoordinator.cpp:
+        (WebCore::MediaSessionCoordinator::join): New.
+        (WebCore::MediaSessionCoordinator::leave): New.
+        (WebCore::MediaSessionCoordinator::seekTo): Reject unless state is 'joined'. Don't
+        call session method.
+        (WebCore::MediaSessionCoordinator::play): Ditto.
+        (WebCore::MediaSessionCoordinator::pause): Ditto.
+        (WebCore::MediaSessionCoordinator::setTrack): Ditto.
+        (WebCore::MediaSessionCoordinator::positionStateChanged):
+        (WebCore::MediaSessionCoordinator::playbackStateChanged):
+        (WebCore::MediaSessionCoordinator::readyStateChanged): Do nothing unless state is
+        'joined'. Improve logging.
+        (WebCore::MediaSessionCoordinator::seekSessionToTime): Ditto.
+        (WebCore::MediaSessionCoordinator::playSession): Ditto.
+        (WebCore::MediaSessionCoordinator::pauseSession): Ditto.
+        (WebCore::MediaSessionCoordinator::setSessionTrack): Ditto.
+        * Modules/mediasession/MediaSessionCoordinator.h:
+        (WebCore::MediaSessionCoordinator::identifier const):
+        (WebCore::MediaSessionCoordinator::state const):
+        * Modules/mediasession/MediaSessionCoordinator.idl:
+        * Modules/mediasession/MediaSessionCoordinatorMixin.idl: Fix Conditional.
+
+        * Modules/mediasession/MediaSessionCoordinatorPrivate.h: Declare new required methods.
+
+        * Modules/mediasession/MediaSessionCoordinatorState.h: Define states.
+        * Modules/mediasession/MediaSessionCoordinatorState.idl:
+
+        * Modules/mediasession/MediaSessionPlaylistMixin.idl: Fix Conditional.
+
+        * Sources.txt: Add JSMediaSessionCoordinatorState.cpp.
+
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * testing/MockMediaSessionCoordinator.cpp:
+        (WebCore::MockMediaSessionCoordinator::join):
+        (WebCore::MockMediaSessionCoordinator::leave):
+        (WebCore::MockMediaSessionCoordinator::coordinatorStateChanged):
+        * testing/MockMediaSessionCoordinator.h:
+
 2021-03-31  Zalan Bujtas  <za...@apple.com>
 
         Remove misleading FIXME comment in RenderBox::shouldComputeLogicalWidthFromAspectRatioAndInsets

Modified: trunk/Source/WebCore/DerivedSources-input.xcfilelist (275313 => 275314)


--- trunk/Source/WebCore/DerivedSources-input.xcfilelist	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/DerivedSources-input.xcfilelist	2021-03-31 23:11:14 UTC (rev 275314)
@@ -180,6 +180,7 @@
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionActionHandler.idl
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionCoordinator.idl
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionCoordinatorMixin.idl
+$(PROJECT_DIR)/Modules/mediasession/MediaSessionCoordinatorState.idl
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionPlaybackState.idl
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionPlaylistMixin.idl
 $(PROJECT_DIR)/Modules/mediasession/MediaSessionReadyState.idl

Modified: trunk/Source/WebCore/DerivedSources-output.xcfilelist (275313 => 275314)


--- trunk/Source/WebCore/DerivedSources-output.xcfilelist	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/DerivedSources-output.xcfilelist	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1318,6 +1318,8 @@
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionCoordinator.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionCoordinatorMixin.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionCoordinatorMixin.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionCoordinatorState.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionCoordinatorState.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionPlaybackState.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionPlaybackState.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSMediaSessionPlaylistMixin.cpp

Modified: trunk/Source/WebCore/DerivedSources.make (275313 => 275314)


--- trunk/Source/WebCore/DerivedSources.make	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/DerivedSources.make	2021-03-31 23:11:14 UTC (rev 275314)
@@ -214,6 +214,7 @@
     $(WebCore)/Modules/mediasession/MediaSessionActionHandler.idl \
     $(WebCore)/Modules/mediasession/MediaSessionCoordinator.idl \
     $(WebCore)/Modules/mediasession/MediaSessionCoordinatorMixin.idl \
+    $(WebCore)/Modules/mediasession/MediaSessionCoordinatorState.idl \
     $(WebCore)/Modules/mediasession/MediaSessionPlaybackState.idl \
     $(WebCore)/Modules/mediasession/MediaSessionPlaylistMixin.idl \
     $(WebCore)/Modules/mediasession/MediaSessionReadyState.idl \

Modified: trunk/Source/WebCore/Modules/mediasession/MediaMetadata.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaMetadata.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaMetadata.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -33,8 +33,6 @@
     attribute DOMString artist;
     attribute DOMString album;
     [SetterCallWith=ScriptExecutionContext] attribute FrozenArray<MediaImage> artwork;
-
-    [Conditional=MEDIA_SESSION_PLAYLIST, EnabledBySetting=MediaSessionPlaylist] attribute DOMString trackIdentifier;
 };
 
 MediaMetadata includes MediaMetadataPlaylistMixin;

Modified: trunk/Source/WebCore/Modules/mediasession/MediaMetadataPlaylistMixin.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaMetadataPlaylistMixin.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaMetadataPlaylistMixin.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -24,7 +24,7 @@
  */
 
 [
-    Conditional=ENABLE_MEDIA_SESSION_PLAYLIST,
+    Conditional=MEDIA_SESSION_PLAYLIST,
     EnabledBySetting=MediaSessionPlaylist,
 ] interface mixin MediaMetadataPlaylistMixin {
     attribute DOMString trackIdentifier;

Modified: trunk/Source/WebCore/Modules/mediasession/MediaPositionState.h (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaPositionState.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaPositionState.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -34,6 +34,8 @@
     double playbackRate = 1;
     double position = 0;
 
+    String toJSONString() const;
+
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static Optional<MediaPositionState> decode(Decoder&);
 };
@@ -62,4 +64,14 @@
 
 }
 
+namespace WTF {
+
+template<typename> struct LogArgument;
+
+template<> struct LogArgument<WebCore::MediaPositionState> {
+    static String toString(const WebCore::MediaPositionState& state) { return state.toJSONString(); }
+};
+
+} // namespace WTF
+
 #endif // ENABLE(MEDIA_SESSION)

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -38,6 +38,7 @@
 #include "MediaSessionCoordinator.h"
 #include "Navigator.h"
 #include "PlatformMediaSessionManager.h"
+#include <wtf/JSONValues.h>
 
 namespace WebCore {
 
@@ -81,14 +82,13 @@
 }
 
 MediaSession::MediaSession(Navigator& navigator)
-    : ContextDestructionObserver(navigator.scriptExecutionContext())
+    : ActiveDOMObject(navigator.scriptExecutionContext())
     , m_navigator(makeWeakPtr(navigator))
-#if ENABLE(MEDIA_SESSION_COORDINATOR)
     , m_asyncEventQueue(MainThreadGenericEventQueue::create(*this))
-#endif
 {
     m_logger = makeRefPtr(Document::sharedLogger());
     m_logIdentifier = nextLogIdentifier();
+    suspendIfNeeded();
 
     ALWAYS_LOG(LOGIDENTIFIER);
 }
@@ -95,6 +95,11 @@
 
 MediaSession::~MediaSession() = default;
 
+bool MediaSession::virtualHasPendingActivity() const
+{
+    return m_asyncEventQueue->hasPendingActivity();
+}
+
 void MediaSession::setMetadata(RefPtr<MediaMetadata>&& metadata)
 {
     ALWAYS_LOG(LOGIDENTIFIER);
@@ -212,7 +217,10 @@
 
 ExceptionOr<void> MediaSession::setPositionState(Optional<MediaPositionState>&& state)
 {
-    ALWAYS_LOG(LOGIDENTIFIER);
+    if (state)
+        ALWAYS_LOG(LOGIDENTIFIER, state.value());
+    else
+        ALWAYS_LOG(LOGIDENTIFIER, "{ }");
 
     if (!state) {
         m_positionState = WTF::nullopt;
@@ -315,6 +323,17 @@
 }
 #endif
 
+String MediaPositionState::toJSONString() const
+{
+    auto object = JSON::Object::create();
+
+    object->setDouble("duration"_s, duration);
+    object->setDouble("playbackRate"_s, playbackRate);
+    object->setDouble("position"_s, position);
+
+    return object->toJSONString();
 }
 
+}
+
 #endif // ENABLE(MEDIA_SESSION)

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.h (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSession.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -27,7 +27,7 @@
 
 #if ENABLE(MEDIA_SESSION)
 
-#include "ContextDestructionObserver.h"
+#include "ActiveDOMObject.h"
 #include "EventTarget.h"
 #include "GenericEventQueue.h"
 #include "MediaPositionState.h"
@@ -49,7 +49,7 @@
 class MediaSessionCoordinator;
 class Navigator;
 
-class MediaSession : public RefCounted<MediaSession> , public ContextDestructionObserver , public EventTargetWithInlineData {
+class MediaSession : public RefCounted<MediaSession>, public ActiveDOMObject, public EventTargetWithInlineData {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     static Ref<MediaSession> create(Navigator&);
@@ -124,11 +124,15 @@
     void notifyActionHandlerObservers();
     void notifyReadyStateObservers();
 
+    // EventTarget
+    void refEventTarget() final { ref(); }
+    void derefEventTarget() final { deref(); }
     EventTargetInterface eventTargetInterface() const final { return MediaSessionEventTargetInterfaceType; }
     ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }
 
-    void refEventTarget() final { ref(); }
-    void derefEventTarget() final { deref(); }
+    // ActiveDOMObject
+    const char* activeDOMObjectName() const final { return "MediaSession"; }
+    bool virtualHasPendingActivity() const final;
 
     WeakPtr<Navigator> m_navigator;
     RefPtr<MediaMetadata> m_metadata;
@@ -141,11 +145,11 @@
     const void* m_logIdentifier;
 
     WeakHashSet<Observer> m_observers;
+    UniqueRef<MainThreadGenericEventQueue> m_asyncEventQueue;
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
     MediaSessionReadyState m_readyState { MediaSessionReadyState::HaveNothing };
     RefPtr<MediaSessionCoordinator> m_coordinator;
-    UniqueRef<MainThreadGenericEventQueue> m_asyncEventQueue;
 #endif
 
 #if ENABLE(MEDIA_SESSION_PLAYLIST)

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSession.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSession.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSession.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=MEDIA_SESSION,
     Exposed=Window,
     ExportMacro=WEBCORE_EXPORT,

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.cpp (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -30,6 +30,7 @@
 
 #include "JSDOMException.h"
 #include "JSDOMPromiseDeferred.h"
+#include "JSMediaSessionCoordinatorState.h"
 #include "Logging.h"
 #include "MediaSession.h"
 #include "MediaSessionCoordinatorPrivate.h"
@@ -61,6 +62,48 @@
 
 MediaSessionCoordinator::~MediaSessionCoordinator() = default;
 
+void MediaSessionCoordinator::join(DOMPromiseDeferred<void>&& promise)
+{
+    auto identifier = LOGIDENTIFIER;
+    ALWAYS_LOG(identifier, m_state);
+
+    if (m_state != MediaSessionCoordinatorState::Waiting) {
+        ERROR_LOG(identifier, "invalid state");
+        promise.reject(Exception { InvalidStateError, makeString("Unable to join when state is ", convertEnumerationToString(m_state)) });
+        return;
+    }
+
+    m_privateCoordinator->join([protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
+        if (!protectedThis->m_session) {
+            promise.reject(Exception { InvalidStateError });
+            return;
+        }
+
+        if (exception) {
+            protectedThis->logger().error(protectedThis->logChannel(), identifier, "coordinator.join failed!");
+            promise.reject(WTFMove(*exception));
+            return;
+        }
+
+        protectedThis->m_state = MediaSessionCoordinatorState::Joined;
+        protectedThis->m_privateCoordinator->coordinatorStateChanged(MediaSessionCoordinatorState::Joined);
+        promise.resolve();
+    });
+}
+
+ExceptionOr<void> MediaSessionCoordinator::leave()
+{
+    ALWAYS_LOG(LOGIDENTIFIER);
+    if (m_state != MediaSessionCoordinatorState::Joined)
+        return Exception { InvalidStateError, makeString("Unable to leave when state is ", convertEnumerationToString(m_state)) };
+
+    m_state = MediaSessionCoordinatorState::Closed;
+    m_privateCoordinator->leave();
+    m_privateCoordinator->coordinatorStateChanged(MediaSessionCoordinatorState::Closed);
+
+    return { };
+}
+
 void MediaSessionCoordinator::seekTo(double time, DOMPromiseDeferred<void>&& promise)
 {
     auto identifier = LOGIDENTIFIER;
@@ -72,7 +115,13 @@
         return;
     }
 
-    m_privateCoordinator->seekTo(time, [time, protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        ERROR_LOG(identifier, ".state is ", m_state);
+        promise.reject(Exception { InvalidStateError, makeString("Unable to seekTo when state is ", convertEnumerationToString(m_state)) });
+        return;
+    }
+
+    m_privateCoordinator->seekTo(time, [protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
         if (!protectedThis->m_session) {
             promise.reject(Exception { InvalidStateError });
             return;
@@ -84,9 +133,6 @@
             return;
         }
 
-        // FIXME: should the promise reject if there isn't a registered 'seekTo' action handler?
-        protectedThis->internalSeekTo(time);
-
         promise.resolve();
     });
 }
@@ -102,6 +148,12 @@
         return;
     }
 
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        ERROR_LOG(identifier, ".state is ", m_state);
+        promise.reject(Exception { InvalidStateError, makeString("Unable to play when state is ", convertEnumerationToString(m_state)) });
+        return;
+    }
+
     m_privateCoordinator->play([protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
         if (!protectedThis->m_session) {
             promise.reject(Exception { InvalidStateError });
@@ -114,9 +166,6 @@
             return;
         }
 
-        // FIXME: should the promise reject if there isn't a registered 'play' action handler?
-        protectedThis->internalPlay();
-
         promise.resolve();
     });
 }
@@ -132,6 +181,12 @@
         return;
     }
 
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        ERROR_LOG(identifier, ".state is ", m_state);
+        promise.reject(Exception { InvalidStateError, makeString("Unable to pause when state is ", convertEnumerationToString(m_state)) });
+        return;
+    }
+
     m_privateCoordinator->pause([protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
         if (!protectedThis->m_session) {
             promise.reject(Exception { InvalidStateError });
@@ -144,9 +199,6 @@
             return;
         }
 
-        // FIXME: should the promise reject if there isn't a registered 'pause' action handler?
-        protectedThis->internalPause();
-
         promise.resolve();
     });
 }
@@ -162,7 +214,13 @@
         return;
     }
 
-    m_privateCoordinator->setTrack(track, [track, protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        ERROR_LOG(identifier, ".state is ", m_state);
+        promise.reject(Exception { InvalidStateError, makeString("Unable to setTrack when state is ", convertEnumerationToString(m_state)) });
+        return;
+    }
+
+    m_privateCoordinator->setTrack(track, [protectedThis = makeRefPtr(*this), identifier, promise = WTFMove(promise)] (Optional<Exception>&& exception) mutable {
         if (!protectedThis->m_session) {
             promise.reject(Exception { InvalidStateError });
             return;
@@ -174,9 +232,6 @@
             return;
         }
 
-        // FIXME: should the promise reject if there isn't a registered 'setTrack' action handler?
-        protectedThis->internalSetTrack(track);
-
         promise.resolve();
     });
 }
@@ -190,43 +245,89 @@
         m_session->addObserver(*this);
 }
 
-void MediaSessionCoordinator::positionStateChanged(const Optional<MediaPositionState>& state)
+void MediaSessionCoordinator::positionStateChanged(const Optional<MediaPositionState>& positionState)
 {
-    if (!state) {
+    if (positionState)
+        ALWAYS_LOG(LOGIDENTIFIER, positionState.value());
+    else
+        ALWAYS_LOG(LOGIDENTIFIER, "{ }");
+
+    if (m_state != MediaSessionCoordinatorState::Joined)
+        return;
+
+    if (!positionState) {
         m_privateCoordinator->positionStateChanged({ });
         return;
     }
 
-    m_privateCoordinator->positionStateChanged(MediaPositionState { state->duration, state->playbackRate, state->position });
+    m_privateCoordinator->positionStateChanged(MediaPositionState { positionState->duration, positionState->playbackRate, positionState->position });
 }
 
-void MediaSessionCoordinator::playbackStateChanged(MediaSessionPlaybackState state)
+void MediaSessionCoordinator::playbackStateChanged(MediaSessionPlaybackState playbackState)
 {
-    m_privateCoordinator->playbackStateChanged(state);
+    ALWAYS_LOG(LOGIDENTIFIER, m_state, ", ", playbackState);
+
+    if (m_state != MediaSessionCoordinatorState::Joined)
+        return;
+
+    m_privateCoordinator->playbackStateChanged(playbackState);
 }
 
-void MediaSessionCoordinator::readyStateChanged(MediaSessionReadyState state)
+void MediaSessionCoordinator::readyStateChanged(MediaSessionReadyState readyState)
 {
-    m_privateCoordinator->readyStateChanged(state);
+    ALWAYS_LOG(LOGIDENTIFIER, m_state, ", ", readyState);
+
+    if (m_state != MediaSessionCoordinatorState::Joined)
+        return;
+
+    m_privateCoordinator->readyStateChanged(readyState);
 }
 
 void MediaSessionCoordinator::seekSessionToTime(double time, CompletionHandler<void(bool)>&& completionHandler)
 {
+    ALWAYS_LOG(LOGIDENTIFIER, m_state, ", ", time);
+
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        completionHandler(false);
+        return;
+    }
+
     completionHandler(internalSeekTo(time));
 }
 
 void MediaSessionCoordinator::playSession(CompletionHandler<void(bool)>&& completionHandler)
 {
+    ALWAYS_LOG(LOGIDENTIFIER, m_state);
+
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        completionHandler(false);
+        return;
+    }
+
     completionHandler(internalPlay());
 }
 
 void MediaSessionCoordinator::pauseSession(CompletionHandler<void(bool)> completionHandler)
 {
+    ALWAYS_LOG(LOGIDENTIFIER, m_state);
+
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        completionHandler(false);
+        return;
+    }
+
     completionHandler(internalPause());
 }
 
 void MediaSessionCoordinator::setSessionTrack(const String& track, CompletionHandler<void(bool)> completionHandler)
 {
+    ALWAYS_LOG(LOGIDENTIFIER, m_state, ", ", track);
+
+    if (m_state != MediaSessionCoordinatorState::Joined) {
+        completionHandler(false);
+        return;
+    }
+
     completionHandler(internalSetTrack(track));
 }
 

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.h (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -29,6 +29,7 @@
 
 #include "MediaSession.h"
 #include "MediaSessionCoordinatorPrivate.h"
+#include "MediaSessionCoordinatorState.h"
 #include <wtf/Logger.h>
 #include <wtf/Optional.h>
 #include <wtf/UniqueRef.h>
@@ -45,6 +46,12 @@
     WEBCORE_EXPORT static Ref<MediaSessionCoordinator> create(Ref<MediaSessionCoordinatorPrivate>&&);
     WEBCORE_EXPORT ~MediaSessionCoordinator();
 
+    void join(DOMPromiseDeferred<void>&&);
+    ExceptionOr<void> leave();
+
+    String identifier() const { return m_privateCoordinator->identifier(); }
+    MediaSessionCoordinatorState state() const { return m_state; }
+
     void seekTo(double, DOMPromiseDeferred<void>&&);
     void play(DOMPromiseDeferred<void>&&);
     void pause(DOMPromiseDeferred<void>&&);
@@ -80,6 +87,7 @@
     static const char* logClassName() { return "MediaSessionCoordinator"; }
 
     WeakPtr<MediaSession> m_session;
+    MediaSessionCoordinatorState m_state { MediaSessionCoordinatorState::Waiting };
     Ref<MediaSessionCoordinatorPrivate> m_privateCoordinator;
     Ref<const Logger> m_logger;
     const void* m_logIdentifier;

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinator.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -29,6 +29,13 @@
     Exposed=Window,
     ExportMacro=WEBCORE_EXPORT,
 ] interface MediaSessionCoordinator {
+
+    Promise<undefined> join();
+    undefined leave();
+
+    readonly attribute MediaSessionCoordinatorState state;
+    readonly attribute DOMString? identifier;
+
     Promise<undefined> seekTo(unrestricted double time);
     Promise<undefined> play();
     Promise<undefined> pause();

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorMixin.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorMixin.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorMixin.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -24,7 +24,7 @@
  */
 
 [
-    Conditional=ENABLE_MEDIA_SESSION_COORDINATOR,
+    Conditional=MEDIA_SESSION_COORDINATOR,
     EnabledBySetting=MediaSessionCoordinator,
 ] interface mixin MediaSessionCoordinatorMixin {
     readonly attribute MediaSessionCoordinator? coordinator;

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorPrivate.h (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorPrivate.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorPrivate.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -29,6 +29,7 @@
 
 #include "Exception.h"
 #include "MediaPositionState.h"
+#include "MediaSessionCoordinatorState.h"
 #include "MediaSessionPlaybackState.h"
 #include "MediaSessionReadyState.h"
 #include <wtf/Optional.h>
@@ -53,6 +54,11 @@
 public:
     virtual ~MediaSessionCoordinatorPrivate() = default;
 
+    virtual String identifier() const = 0;
+
+    virtual void join(CompletionHandler<void(Optional<Exception>&&)>&&) = 0;
+    virtual void leave() = 0;
+
     virtual void seekTo(double, CompletionHandler<void(Optional<Exception>&&)>&&) = 0;
     virtual void play(CompletionHandler<void(Optional<Exception>&&)>&&) = 0;
     virtual void pause(CompletionHandler<void(Optional<Exception>&&)>&&) = 0;
@@ -61,6 +67,7 @@
     virtual void positionStateChanged(const Optional<MediaPositionState>&) = 0;
     virtual void readyStateChanged(MediaSessionReadyState) = 0;
     virtual void playbackStateChanged(MediaSessionPlaybackState) = 0;
+    virtual void coordinatorStateChanged(MediaSessionCoordinatorState) = 0;
 
     void setLogger(const WTF::Logger&, const void*);
     virtual void setClient(WeakPtr<MediaSessionCoordinatorClient> client) { m_client = client;}

Copied: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.h (from rev 275313, trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorMixin.idl) (0 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.h	                        (rev 0)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 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
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SESSION_COORDINATOR)
+
+namespace WebCore {
+
+enum class MediaSessionCoordinatorState : uint8_t {
+    Waiting,
+    Joined,
+    Closed,
+};
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::MediaSessionCoordinatorState> {
+    using values = EnumValues<
+        WebCore::MediaSessionCoordinatorState,
+        WebCore::MediaSessionCoordinatorState::Waiting,
+        WebCore::MediaSessionCoordinatorState::Joined,
+        WebCore::MediaSessionCoordinatorState::Closed
+    >;
+};
+
+}
+
+#endif // ENABLE(MEDIA_SESSION_COORDINATOR)

Copied: trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.idl (from rev 275313, trunk/Source/WebCore/Modules/mediasession/MediaMetadataPlaylistMixin.idl) (0 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.idl	                        (rev 0)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionCoordinatorState.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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
+ * 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.
+ */
+
+[
+    Conditional=MEDIA_SESSION_COORDINATOR,
+] enum MediaSessionCoordinatorState {
+    "waiting",
+    "joined",
+    "closed"
+};

Modified: trunk/Source/WebCore/Modules/mediasession/MediaSessionPlaylistMixin.idl (275313 => 275314)


--- trunk/Source/WebCore/Modules/mediasession/MediaSessionPlaylistMixin.idl	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Modules/mediasession/MediaSessionPlaylistMixin.idl	2021-03-31 23:11:14 UTC (rev 275314)
@@ -24,7 +24,7 @@
  */
 
 [
-    Conditional=ENABLE_MEDIA_SESSION_PLAYLIST,
+    Conditional=MEDIA_SESSION_PLAYLIST,
     EnabledBySetting=MediaSessionPlaylist,
 ] interface mixin MediaSessionPlaylistMixin {
     [SetterCallWith=ScriptExecutionContext] attribute FrozenArray<MediaMetadata> playlist;

Modified: trunk/Source/WebCore/Sources.txt (275313 => 275314)


--- trunk/Source/WebCore/Sources.txt	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/Sources.txt	2021-03-31 23:11:14 UTC (rev 275314)
@@ -3233,6 +3233,7 @@
 JSMediaList.cpp
 JSMediaMetadata.cpp
 JSMediaSessionCoordinator.cpp
+JSMediaSessionCoordinatorState.cpp
 JSMediaMetadataInit.cpp
 JSMediaPositionState.cpp
 JSMediaQueryList.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (275313 => 275314)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-03-31 23:11:14 UTC (rev 275314)
@@ -218,6 +218,7 @@
 		07B93FFC23B94EC70036F8EA /* MIMETypeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B93FF923B92AAA0036F8EA /* MIMETypeCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		07C046C41E42508B007201E7 /* CAAudioStreamDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 073B87581E40DCFD0071C0EC /* CAAudioStreamDescription.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		07C046CB1E426413007201E7 /* AudioStreamDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 073B87561E40DCE50071C0EC /* AudioStreamDescription.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		07C1482E2612A21F00775828 /* MediaSessionCoordinatorState.h in Headers */ = {isa = PBXBuildFile; fileRef = 077BA58826126D660072F19F /* MediaSessionCoordinatorState.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		07C1C0E21BFB600100BD2256 /* MediaTrackSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C1C0E01BFB600100BD2256 /* MediaTrackSupportedConstraints.h */; };
 		07C1C0E51BFB60ED00BD2256 /* RealtimeMediaSourceSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C1C0E41BFB60ED00BD2256 /* RealtimeMediaSourceSupportedConstraints.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		07CB9E72249C36B200A69489 /* AVRoutePickerViewTargetPicker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07CB9E6F249C226C00A69489 /* AVRoutePickerViewTargetPicker.mm */; };
@@ -5861,6 +5862,8 @@
 		077B64151B95F703003E9AD5 /* MediaPlaybackTargetMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPlaybackTargetMock.h; sourceTree = "<group>"; };
 		077BA57C260FE7380072F19F /* MediaSessionCoordinatorPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaSessionCoordinatorPrivate.h; sourceTree = "<group>"; };
 		077BA57D260FE7390072F19F /* MediaSessionCoordinatorPrivate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaSessionCoordinatorPrivate.cpp; sourceTree = "<group>"; };
+		077BA58826126D660072F19F /* MediaSessionCoordinatorState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaSessionCoordinatorState.h; sourceTree = "<group>"; };
+		077BA58A26126D6B0072F19F /* MediaSessionCoordinatorState.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = MediaSessionCoordinatorState.idl; sourceTree = "<group>"; };
 		0783228218013ED700999E0C /* MediaStreamAudioSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaStreamAudioSource.cpp; sourceTree = "<group>"; };
 		0783228318013ED800999E0C /* MediaStreamAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaStreamAudioSource.h; sourceTree = "<group>"; };
 		07846340145B151A00A58DF1 /* JSTrackEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTrackEvent.cpp; sourceTree = "<group>"; };
@@ -5931,6 +5934,10 @@
 		07B7116C1D899E63009F0FFB /* CaptureDeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaptureDeviceManager.h; sourceTree = "<group>"; };
 		07B93FF923B92AAA0036F8EA /* MIMETypeCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MIMETypeCache.h; sourceTree = "<group>"; };
 		07B93FFB23B92AAB0036F8EA /* MIMETypeCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MIMETypeCache.cpp; sourceTree = "<group>"; };
+		07C148292612955C00775828 /* JSMediaSessionReadyState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSMediaSessionReadyState.h; sourceTree = "<group>"; };
+		07C1482B2612955D00775828 /* JSMediaSessionReadyState.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSMediaSessionReadyState.cpp; sourceTree = "<group>"; };
+		07C1482C2612955E00775828 /* JSMediaSessionCoordinatorState.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSMediaSessionCoordinatorState.cpp; sourceTree = "<group>"; };
+		07C1482D2612955F00775828 /* JSMediaSessionCoordinatorState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSMediaSessionCoordinatorState.h; sourceTree = "<group>"; };
 		07C1C0E01BFB600100BD2256 /* MediaTrackSupportedConstraints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaTrackSupportedConstraints.h; sourceTree = "<group>"; };
 		07C1C0E11BFB600100BD2256 /* MediaTrackSupportedConstraints.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MediaTrackSupportedConstraints.idl; sourceTree = "<group>"; };
 		07C1C0E41BFB60ED00BD2256 /* RealtimeMediaSourceSupportedConstraints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealtimeMediaSourceSupportedConstraints.h; sourceTree = "<group>"; };
@@ -28412,6 +28419,8 @@
 				0736B73026094A3700E06994 /* MediaSessionCoordinatorMixin.idl */,
 				077BA57D260FE7390072F19F /* MediaSessionCoordinatorPrivate.cpp */,
 				077BA57C260FE7380072F19F /* MediaSessionCoordinatorPrivate.h */,
+				077BA58826126D660072F19F /* MediaSessionCoordinatorState.h */,
+				077BA58A26126D6B0072F19F /* MediaSessionCoordinatorState.idl */,
 				CDDDEA292538CDCB00A1300C /* MediaSessionPlaybackState.h */,
 				CDDDEA282538CDCB00A1300C /* MediaSessionPlaybackState.idl */,
 				0736B7322609555400E06994 /* MediaSessionPlaylistMixin.idl */,
@@ -28445,8 +28454,12 @@
 				CDDDEA4E253913BC00A1300C /* JSMediaSessionActionHandler.h */,
 				072DBC0725DC6EDA00A1350E /* JSMediaSessionCoordinator.cpp */,
 				072DBC0625DC6EDA00A1350E /* JSMediaSessionCoordinator.h */,
+				07C1482C2612955E00775828 /* JSMediaSessionCoordinatorState.cpp */,
+				07C1482D2612955F00775828 /* JSMediaSessionCoordinatorState.h */,
 				CDDDEA46253911DD00A1300C /* JSMediaSessionPlaybackState.cpp */,
 				CDDDEA42253911DC00A1300C /* JSMediaSessionPlaybackState.h */,
+				07C1482B2612955D00775828 /* JSMediaSessionReadyState.cpp */,
+				07C148292612955C00775828 /* JSMediaSessionReadyState.h */,
 			);
 			name = MediaSession;
 			sourceTree = "<group>";
@@ -34018,6 +34031,7 @@
 				077BA578260FAEDB0072F19F /* MediaSessionActionHandler.h in Headers */,
 				077BA571260F9F760072F19F /* MediaSessionCoordinator.h in Headers */,
 				077BA57E260FEF360072F19F /* MediaSessionCoordinatorPrivate.h in Headers */,
+				07C1482E2612A21F00775828 /* MediaSessionCoordinatorState.h in Headers */,
 				416ECAE525B58CC400B34DA5 /* MediaSessionGroupIdentifier.h in Headers */,
 				CDA9593F2412BAE000910EEF /* MediaSessionHelperIOS.h in Headers */,
 				414460A22412994500814BE7 /* MediaSessionIdentifier.h in Headers */,

Modified: trunk/Source/WebCore/testing/MockMediaSessionCoordinator.cpp (275313 => 275314)


--- trunk/Source/WebCore/testing/MockMediaSessionCoordinator.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/testing/MockMediaSessionCoordinator.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -59,6 +59,17 @@
     return WTF::nullopt;
 }
 
+void MockMediaSessionCoordinator::join(CompletionHandler<void(Optional<Exception>&&)>&& callback)
+{
+    m_context->postTask([this, callback = WTFMove(callback)] (ScriptExecutionContext&) mutable {
+        callback(result());
+    });
+}
+
+void MockMediaSessionCoordinator::leave()
+{
+}
+
 void MockMediaSessionCoordinator::seekTo(double time, CompletionHandler<void(Optional<Exception>&&)>&& callback)
 {
     ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER, time);
@@ -109,6 +120,12 @@
     m_stateChangeListener->scheduleCallback(m_context.get(), "playbackStateChanged");
 }
 
+void MockMediaSessionCoordinator::coordinatorStateChanged(MediaSessionCoordinatorState state)
+{
+    ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER, state);
+    m_stateChangeListener->scheduleCallback(m_context.get(), "coordinatorStateChanged");
+}
+
 WTFLogChannel& MockMediaSessionCoordinator::logChannel() const
 {
     return LogMedia;

Modified: trunk/Source/WebCore/testing/MockMediaSessionCoordinator.h (275313 => 275314)


--- trunk/Source/WebCore/testing/MockMediaSessionCoordinator.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebCore/testing/MockMediaSessionCoordinator.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -47,6 +47,11 @@
 private:
     MockMediaSessionCoordinator(ScriptExecutionContext&, RefPtr<StringCallback>&&);
 
+    String identifier() const final { return "Mock Coordinator"; }
+
+    void join(CompletionHandler<void(Optional<Exception>&&)>&&) final;
+    void leave() final;
+
     void seekTo(double, CompletionHandler<void(Optional<Exception>&&)>&&) final;
     void play(CompletionHandler<void(Optional<Exception>&&)>&&) final;
     void pause(CompletionHandler<void(Optional<Exception>&&)>&&) final;
@@ -55,6 +60,7 @@
     void positionStateChanged(const Optional<MediaPositionState>&) final;
     void readyStateChanged(MediaSessionReadyState) final;
     void playbackStateChanged(MediaSessionPlaybackState) final;
+    void coordinatorStateChanged(MediaSessionCoordinatorState) final;
 
     const char* logClassName() const { return "MockMediaSessionCoordinator"; }
     WTFLogChannel& logChannel() const;

Modified: trunk/Source/WebKit/ChangeLog (275313 => 275314)


--- trunk/Source/WebKit/ChangeLog	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/ChangeLog	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1,3 +1,57 @@
+2021-03-31  Eric Carlson  <eric.carl...@apple.com>
+
+        [macOS] MediaSessionCoordinator should have join and leave methods
+        https://bugs.webkit.org/show_bug.cgi?id=223955
+        <rdar://problem/76021588>
+
+        Reviewed by Jer Noble.
+
+        * UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h: Renamed from Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h.
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp:
+        (WebKit::RemoteMediaSessionCoordinatorProxy::create):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::join):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::leave):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::coordinatorStateChanged):
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h:
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::createMediaSessionCoordinator):
+        * UIProcess/WebPageProxy.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp:
+        (WebKit::RemoteMediaSessionCoordinator::create):
+        (WebKit::RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator):
+        (WebKit::RemoteMediaSessionCoordinator::join):
+        (WebKit::RemoteMediaSessionCoordinator::leave):
+        (WebKit::RemoteMediaSessionCoordinator::coordinatorStateChanged):
+        * WebProcess/MediaSession/RemoteMediaSessionCoordinator.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::createMediaSessionCoordinator):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        * UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h: Renamed from Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h.
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp:
+        (WebKit::RemoteMediaSessionCoordinatorProxy::create):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy):
+        (WebKit::RemoteMediaSessionCoordinatorProxy::coordinatorStateChanged):
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h:
+        * UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::createMediaSessionCoordinator):
+        * UIProcess/WebPageProxy.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp:
+        (WebKit::RemoteMediaSessionCoordinator::create):
+        (WebKit::RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator):
+        (WebKit::RemoteMediaSessionCoordinator::coordinatorStateChanged):
+        * WebProcess/MediaSession/RemoteMediaSessionCoordinator.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::createMediaSessionCoordinator):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2021-03-31  Wenson Hsieh  <wenson_hs...@apple.com>
 
         [iOS] Update a couple of icons in the file upload context menu

Deleted: trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 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
- * 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.
- */
-
-#pragma once
-
-#if ENABLE(MEDIA_SESSION_COORDINATOR)
-
-#include <WebCore/MediaSessionCoordinatorPrivate.h>
-#include <wtf/RefPtr.h>
-#include <wtf/WeakPtr.h>
-
-namespace WebKit {
-
-class MediaSessionCoordinatorPrivateProxy
-    : public CanMakeWeakPtr<MediaSessionCoordinatorPrivateProxy>
-    , public RefCounted<MediaSessionCoordinatorPrivateProxy> {
-public:
-    virtual ~MediaSessionCoordinatorPrivateProxy() = default;
-
-    virtual void seekTo(double, CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
-    virtual void play(CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
-    virtual void pause(CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
-    virtual void setTrack(const String&, CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
-
-    virtual void positionStateChanged(const Optional<WebCore::MediaPositionState>&) = 0;
-    virtual void readyStateChanged(WebCore::MediaSessionReadyState) = 0;
-    virtual void playbackStateChanged(WebCore::MediaSessionPlaybackState) = 0;
-
-    virtual void setClient(WeakPtr<WebCore::MediaSessionCoordinatorClient> client) { m_client = client; }
-
-protected:
-    explicit MediaSessionCoordinatorPrivateProxy() = default;
-
-    WeakPtr<WebCore::MediaSessionCoordinatorClient> client() const { return m_client; }
-
-private:
-    WeakPtr<WebCore::MediaSessionCoordinatorClient> m_client;
-};
-
-} // namespace WebKit
-
-#endif // ENABLE(MEDIA_SESSION_COORDINATOR)

Copied: trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h (from rev 275313, trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorPrivateProxy.h) (0 => 275314)


--- trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/Media/MediaSessionCoordinatorProxyPrivate.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SESSION_COORDINATOR)
+
+#include <WebCore/MediaSessionCoordinatorPrivate.h>
+#include <wtf/RefPtr.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebKit {
+
+class MediaSessionCoordinatorProxyPrivate
+    : public CanMakeWeakPtr<MediaSessionCoordinatorProxyPrivate>
+    , public RefCounted<MediaSessionCoordinatorProxyPrivate> {
+public:
+    virtual ~MediaSessionCoordinatorProxyPrivate() = default;
+
+    virtual String identifier() const = 0;
+
+    virtual void seekTo(double, CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
+    virtual void play(CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
+    virtual void pause(CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
+    virtual void setTrack(const String&, CompletionHandler<void(const WebCore::ExceptionData&)>&&) = 0;
+
+    virtual void positionStateChanged(const Optional<WebCore::MediaPositionState>&) = 0;
+    virtual void readyStateChanged(WebCore::MediaSessionReadyState) = 0;
+    virtual void playbackStateChanged(WebCore::MediaSessionPlaybackState) = 0;
+    virtual void coordinatorStateChanged(WebCore::MediaSessionCoordinatorState) = 0;
+
+    virtual void setClient(WeakPtr<WebCore::MediaSessionCoordinatorClient> client) { m_client = client; }
+
+protected:
+    explicit MediaSessionCoordinatorProxyPrivate() = default;
+
+    WeakPtr<WebCore::MediaSessionCoordinatorClient> client() const { return m_client; }
+
+private:
+    WeakPtr<WebCore::MediaSessionCoordinatorClient> m_client;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(MEDIA_SESSION_COORDINATOR)

Modified: trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -28,7 +28,7 @@
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
 
-#include "MediaSessionCoordinatorPrivateProxy.h"
+#include "MediaSessionCoordinatorProxyPrivate.h"
 #include "RemoteMediaSessionCoordinatorMessages.h"
 #include "WebPageProxy.h"
 #include "WebProcessProxy.h"
@@ -39,12 +39,12 @@
 namespace WebKit {
 using namespace WebCore;
 
-RefPtr<RemoteMediaSessionCoordinatorProxy> RemoteMediaSessionCoordinatorProxy::create(WebPageProxy& webPageProxy, Ref<MediaSessionCoordinatorPrivateProxy>&& privateCoordinator)
+RefPtr<RemoteMediaSessionCoordinatorProxy> RemoteMediaSessionCoordinatorProxy::create(WebPageProxy& webPageProxy, Ref<MediaSessionCoordinatorProxyPrivate>&& privateCoordinator)
 {
     return adoptRef(*new RemoteMediaSessionCoordinatorProxy(webPageProxy, WTFMove(privateCoordinator)));
 }
 
-RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy(WebPageProxy& webPageProxy, Ref<MediaSessionCoordinatorPrivateProxy>&& privateCoordinator)
+RemoteMediaSessionCoordinatorProxy::RemoteMediaSessionCoordinatorProxy(WebPageProxy& webPageProxy, Ref<MediaSessionCoordinatorProxyPrivate>&& privateCoordinator)
     : m_webPageProxy(webPageProxy)
     , m_privateCoordinator(WTFMove(privateCoordinator))
     , m_logger(m_webPageProxy.logger())
@@ -58,7 +58,24 @@
     m_webPageProxy.process().removeMessageReceiver(Messages::RemoteMediaSessionCoordinatorProxy::messageReceiverName(), m_webPageProxy.webPageID());
 }
 
+void RemoteMediaSessionCoordinatorProxy::join(CompletionHandler<void(const WebCore::ExceptionData&)>&&)
+{
+    auto identifier = LOGIDENTIFIER;
+    ALWAYS_LOG(identifier);
 
+    m_privateCoordinator->join([identifier] (const WebCore::ExceptionData&& exception) mutable {
+        ALWAYS_LOG(identifier, "completion");
+        completionHandler(WTFMove(exception));
+    });
+}
+
+void RemoteMediaSessionCoordinatorProxy::leave()
+{
+    ALWAYS_LOG(LOGIDENTIFIER);
+
+    m_privateCoordinator->leave();
+}
+
 void RemoteMediaSessionCoordinatorProxy::coordinateSeekTo(double time, CompletionHandler<void(const WebCore::ExceptionData&)>&& completionHandler)
 {
     auto identifier = LOGIDENTIFIER;
@@ -141,6 +158,12 @@
     m_webPageProxy.sendWithAsyncReply(Messages::RemoteMediaSessionCoordinator::SetSessionTrack { trackId }, callback);
 }
 
+void RemoteMediaSessionCoordinatorProxy::coordinatorStateChanged(WebCore::MediaSessionCoordinatorState state)
+{
+    ALWAYS_LOG(LOGIDENTIFIER);
+    m_privateCoordinator->coordinatorStateChanged(state);
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(MEDIA_SESSION_COORDINATOR)

Modified: trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -27,7 +27,7 @@
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
 
-#include "MediaSessionCoordinatorPrivateProxy.h"
+#include "MediaSessionCoordinatorProxyPrivate.h"
 #include "MessageReceiver.h"
 #include <wtf/Forward.h>
 #include <wtf/Noncopyable.h>
@@ -46,19 +46,19 @@
     , public WebCore::MediaSessionCoordinatorClient {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<RemoteMediaSessionCoordinatorProxy> create(WebPageProxy&, Ref<MediaSessionCoordinatorPrivateProxy>&&);
+    static Ref<RemoteMediaSessionCoordinatorProxy> create(WebPageProxy&, Ref<MediaSessionCoordinatorProxyPrivate>&&);
     ~RemoteMediaSessionCoordinatorProxy();
 
-    void seekTo(double);
-    void play();
-    void pause();
-    void setTrack(const String&);
+    void seekTo(double, CompletionHandler<void(bool)>&&);
+    void play(CompletionHandler<void(bool)>&&);
+    void pause(CompletionHandler<void(bool)>&&);
+    void setTrack(const String&, CompletionHandler<void(bool)>&&);
 
     using MediaSessionCoordinatorClient::weakPtrFactory;
     using WeakValueType = MediaSessionCoordinatorClient::WeakValueType;
 
 private:
-    explicit RemoteMediaSessionCoordinatorProxy(WebPageProxy&, Ref<MediaSessionCoordinatorPrivateProxy>&&);
+    explicit RemoteMediaSessionCoordinatorProxy(WebPageProxy&, Ref<MediaSessionCoordinatorProxyPrivate>&&);
 
     using CoordinateCompletionHandler = CompletionHandler<void(const WebCore::ExceptionData&)>;
 
@@ -66,6 +66,8 @@
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     // Receivers.
+    void join(CompletionHandler<void(const WebCore::ExceptionData&)>&&);
+    void leave();
     void coordinateSeekTo(double, CompletionHandler<void(const WebCore::ExceptionData&)>&&);
     void coordinatePlay(CompletionHandler<void(const WebCore::ExceptionData&)>&&);
     void coordinatePause(CompletionHandler<void(const WebCore::ExceptionData&)>&&);
@@ -73,6 +75,7 @@
     void positionStateChanged(Optional<WebCore::MediaPositionState>);
     void readyStateChanged(WebCore::MediaSessionReadyState);
     void playbackStateChanged(WebCore::MediaSessionPlaybackState);
+    void coordinatorStateChanged(WebCore::MediaSessionCoordinatorState);
 
     // MediaSessionCoordinatorClient
     void seekSessionToTime(double, CompletionHandler<void(bool)>&&) final;
@@ -81,7 +84,7 @@
     void setSessionTrack(const String&, CompletionHandler<void(bool)>) final;
 
     WebPageProxy& m_webPageProxy;
-    Ref<MediaSessionCoordinatorPrivateProxy> m_privateCoordinator;
+    Ref<MediaSessionCoordinatorProxyPrivate> m_privateCoordinator;
     Ref<const Logger> m_logger;
     const void* m_logIdentifier;
 };

Modified: trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.messages.in	2021-03-31 23:11:14 UTC (rev 275314)
@@ -26,6 +26,10 @@
 
 messages -> RemoteMediaSessionCoordinatorProxy NotRefCounted {
 
+    Join() -> (Optional<WebCore::ExceptionData> error) Async
+
+    Leave()
+
     CoordinateSeekTo(double time) -> (Optional<WebCore::ExceptionData> error) Async
 
     CoordinatePlay() -> (Optional<WebCore::ExceptionData> error) Async
@@ -39,6 +43,8 @@
     ReadyStateChanged(enum:uint8_t WebCore::MediaSessionReadyState state);
 
     PlaybackStateChanged(enum:uint8_t WebCore::MediaSessionPlaybackState state);
+
+    CoordinatorStateChanged(enum:uint8_t WebCore::MediaSessionCoordinatorState state);
 }
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -297,7 +297,7 @@
 #endif
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-#include "MediaSessionCoordinatorPrivateProxy.h"
+#include "MediaSessionCoordinatorProxyPrivate.h"
 #include "RemoteMediaSessionCoordinatorProxy.h"
 #endif
 
@@ -10383,11 +10383,11 @@
 #endif
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-void WebPageProxy::createMediaSessionCoordinator(Ref<MediaSessionCoordinatorPrivateProxy>&& privateCoordinator, CompletionHandler<void(WeakPtr<RemoteMediaSessionCoordinatorProxy>)>&& completionHandler)
+void WebPageProxy::createMediaSessionCoordinator(Ref<MediaSessionCoordinatorProxyPrivate>&& privateCoordinator, CompletionHandler<void(WeakPtr<RemoteMediaSessionCoordinatorProxy>)>&& completionHandler)
 {
     ASSERT(!m_mediaSessionCoordinatorProxy);
 
-    sendWithAsyncReply(Messages::WebPage::CreateMediaSessionCoordinator(), [weakThis = makeWeakPtr(*this), privateCoordinator = WTFMove(privateCoordinator), completionHandler = WTFMove(completionHandler)](bool success) mutable {
+    sendWithAsyncReply(Messages::WebPage::CreateMediaSessionCoordinator(privateCoordinator->identifier()), [weakThis = makeWeakPtr(*this), privateCoordinator = WTFMove(privateCoordinator), completionHandler = WTFMove(completionHandler)](bool success) mutable {
 
         if (!weakThis || !success) {
             completionHandler({ });

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (275313 => 275314)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -329,7 +329,7 @@
 class NativeWebMouseEvent;
 class NativeWebWheelEvent;
 class PageClient;
-class MediaSessionCoordinatorPrivateProxy;
+class MediaSessionCoordinatorProxyPrivate;
 class ProvisionalPageProxy;
 class RemoteLayerTreeHost;
 class RemoteLayerTreeScrollingPerformanceData;
@@ -1888,7 +1888,7 @@
 #endif
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-    void createMediaSessionCoordinator(Ref<MediaSessionCoordinatorPrivateProxy>&&, CompletionHandler<void(WeakPtr<RemoteMediaSessionCoordinatorProxy>)>&&);
+    void createMediaSessionCoordinator(Ref<MediaSessionCoordinatorProxyPrivate>&&, CompletionHandler<void(WeakPtr<RemoteMediaSessionCoordinatorProxy>)>&&);
 #endif
 
 #if PLATFORM(COCOA)

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (275313 => 275314)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-03-31 23:11:14 UTC (rev 275314)
@@ -2394,7 +2394,7 @@
 		074E76011DF7075D00D318EC /* MediaDeviceSandboxExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaDeviceSandboxExtensions.h; sourceTree = "<group>"; };
 		076E884D1A13CADF005E90FC /* APIContextMenuClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIContextMenuClient.h; sourceTree = "<group>"; };
 		076E884F1A13CBC6005E90FC /* APIInjectedBundlePageContextMenuClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIInjectedBundlePageContextMenuClient.h; sourceTree = "<group>"; };
-		077BA570260E8F630072F19F /* MediaSessionCoordinatorPrivateProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaSessionCoordinatorPrivateProxy.h; sourceTree = "<group>"; };
+		077BA570260E8F630072F19F /* MediaSessionCoordinatorProxyPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaSessionCoordinatorProxyPrivate.h; sourceTree = "<group>"; };
 		07923130239B3B0C009598E2 /* RemoteMediaPlayerManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteMediaPlayerManager.cpp; sourceTree = "<group>"; };
 		07923131239B3B0C009598E2 /* MediaPlayerPrivateRemote.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaPlayerPrivateRemote.cpp; sourceTree = "<group>"; };
 		07923132239B3B0C009598E2 /* MediaPlayerPrivateRemote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaPlayerPrivateRemote.h; sourceTree = "<group>"; };
@@ -11260,7 +11260,7 @@
 				CD4570DD2444CA5800A3DCEB /* AudioSessionRoutingArbitratorProxy.cpp */,
 				CD4570DB2444CA5800A3DCEB /* AudioSessionRoutingArbitratorProxy.h */,
 				CD4570DC2444CA5800A3DCEB /* AudioSessionRoutingArbitratorProxy.messages.in */,
-				077BA570260E8F630072F19F /* MediaSessionCoordinatorPrivateProxy.h */,
+				077BA570260E8F630072F19F /* MediaSessionCoordinatorProxyPrivate.h */,
 				0794B4AE244524B00001B9C4 /* MediaUsageManager.cpp */,
 				0794B4AC24451BB90001B9C4 /* MediaUsageManager.h */,
 				0736B75C260D4A4E00E06994 /* RemoteMediaSessionCoordinatorProxy.cpp */,

Modified: trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp (275313 => 275314)


--- trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -45,13 +45,14 @@
 using namespace PAL;
 using namespace WebCore;
 
-Ref<RemoteMediaSessionCoordinator> RemoteMediaSessionCoordinator::create(WebPage& page)
+Ref<RemoteMediaSessionCoordinator> RemoteMediaSessionCoordinator::create(WebPage& page, const String& identifier)
 {
-    return adoptRef(*new RemoteMediaSessionCoordinator(page));
+    return adoptRef(*new RemoteMediaSessionCoordinator(page, identifier));
 }
 
-RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator(WebPage& page)
+RemoteMediaSessionCoordinator::RemoteMediaSessionCoordinator(WebPage& page, const String& identifier)
     : m_page(page)
+    , m_identifier(identifier)
 {
     WebProcess::singleton().addMessageReceiver(Messages::RemoteMediaSessionCoordinator::messageReceiverName(), m_page.identifier(), *this);
 }
@@ -61,6 +62,29 @@
     WebProcess::singleton().removeMessageReceiver(Messages::RemoteMediaSessionCoordinator::messageReceiverName(), m_page.identifier());
 }
 
+void RemoteMediaSessionCoordinator::join(CompletionHandler<void(Optional<WebCore::Exception>&&)>&& callback)
+{
+    ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER);
+    m_page.sendWithAsyncReply(Messages::RemoteMediaSessionCoordinatorProxy::Join { }, [weakThis = makeWeakPtr(this), callback = WTFMove(callback)](auto&& exception) mutable {
+        if (!weakThis) {
+            callback(Exception { InvalidStateError });
+            return;
+        }
+
+        if (exception) {
+            callback(Exception { exception->code, WTFMove(exception->message) });
+            return;
+        }
+
+        callback({ });
+    }, 0);
+}
+
+void RemoteMediaSessionCoordinator::leave()
+{
+    m_page.send(Messages::RemoteMediaSessionCoordinatorProxy::Leave { }, 0);
+}
+
 void RemoteMediaSessionCoordinator::seekTo(double time, CompletionHandler<void(Optional<WebCore::Exception>&&)>&& callback)
 {
     ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER, time);
@@ -151,6 +175,12 @@
     m_page.send(Messages::RemoteMediaSessionCoordinatorProxy::PlaybackStateChanged { state }, 0);
 }
 
+void RemoteMediaSessionCoordinator::coordinatorStateChanged(WebCore::MediaSessionCoordinatorState state)
+{
+    ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER, state);
+    m_page.send(Messages::RemoteMediaSessionCoordinatorProxy::CoordinatorStateChanged { state }, 0);
+}
+
 void RemoteMediaSessionCoordinator::seekSessionToTime(double time, CompletionHandler<void(bool)>&& completionHandler)
 {
     ALWAYS_LOG_IF_POSSIBLE(LOGIDENTIFIER, time);

Modified: trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.h (275313 => 275314)


--- trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebProcess/MediaSession/RemoteMediaSessionCoordinator.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -44,11 +44,11 @@
 class RemoteMediaSessionCoordinator final : public WebCore::MediaSessionCoordinatorPrivate , public IPC::MessageReceiver {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<RemoteMediaSessionCoordinator> create(WebPage&);
+    static Ref<RemoteMediaSessionCoordinator> create(WebPage&, const String&);
     ~RemoteMediaSessionCoordinator();
 
 private:
-    explicit RemoteMediaSessionCoordinator(WebPage&);
+    explicit RemoteMediaSessionCoordinator(WebPage&, const String&);
 
     // IPC::MessageReceiver.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
@@ -60,6 +60,9 @@
     void setSessionTrack(const String&, CompletionHandler<void(bool)>&&);
 
     // MediaSessionCoordinatorPrivate overrides.
+    String identifier() const final { return m_identifier; }
+    void join(CompletionHandler<void(Optional<WebCore::Exception>&&)>&&) final;
+    void leave() final;
     void seekTo(double, CompletionHandler<void(Optional<WebCore::Exception>&&)>&&) final;
     void play(CompletionHandler<void(Optional<WebCore::Exception>&&)>&&) final;
     void pause(CompletionHandler<void(Optional<WebCore::Exception>&&)>&&) final;
@@ -68,11 +71,13 @@
     void positionStateChanged(const Optional<WebCore::MediaPositionState>&) final;
     void readyStateChanged(WebCore::MediaSessionReadyState) final;
     void playbackStateChanged(WebCore::MediaSessionPlaybackState) final;
+    void coordinatorStateChanged(WebCore::MediaSessionCoordinatorState) final;
 
     const char* logClassName() const { return "RemoteMediaSessionCoordinator"; }
     WTFLogChannel& logChannel() const;
 
     WebPage& m_page;
+    String m_identifier;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (275313 => 275314)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-03-31 23:11:14 UTC (rev 275314)
@@ -7387,7 +7387,7 @@
 #endif
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-void WebPage::createMediaSessionCoordinator(CompletionHandler<void(bool)>&& completionHandler)
+void WebPage::createMediaSessionCoordinator(const String& identifier, CompletionHandler<void(bool)>&& completionHandler)
 {
     auto* document = m_mainFrame->coreFrame()->document();
     if (!document || !document->domWindow()) {
@@ -7396,7 +7396,7 @@
     }
 
     auto& session = NavigatorMediaSession::mediaSession(document->domWindow()->navigator());
-    auto coordinator = RemoteMediaSessionCoordinator::create(*this);
+    auto coordinator = RemoteMediaSessionCoordinator::create(*this, identifier);
     m_remoteMediaSessionCoordinator = coordinator.ptr();
     m_mediaSessionCoordinator = MediaSessionCoordinator::create(coordinator.get());
     session.setCoordinator(m_mediaSessionCoordinator.get());

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (275313 => 275314)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-03-31 23:11:14 UTC (rev 275314)
@@ -1413,7 +1413,7 @@
 #endif
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-    void createMediaSessionCoordinator(CompletionHandler<void(bool)>&&);
+    void createMediaSessionCoordinator(const String&, CompletionHandler<void(bool)>&&);
 #endif
 
     void setLastNavigationWasAppBound(bool wasAppBound) { m_lastNavigationWasAppBound = wasAppBound; }

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (275313 => 275314)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2021-03-31 23:05:02 UTC (rev 275313)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2021-03-31 23:11:14 UTC (rev 275314)
@@ -635,7 +635,7 @@
     LastNavigationWasAppBound() -> (bool wasAppBound) Async
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR)
-    CreateMediaSessionCoordinator() -> (bool success) Async
+    CreateMediaSessionCoordinator(String identifier) -> (bool success) Async
 #endif
 
 #if ENABLE(IMAGE_EXTRACTION)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to