Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 1392437bb060048807e94d171c0fdf45e7d27301
https://github.com/WebKit/WebKit/commit/1392437bb060048807e94d171c0fdf45e7d27301
Author: Eric Carlson <[email protected]>
Date: 2026-08-30 (Sun, 30 Aug 2026)
Changed paths:
M
LayoutTests/http/tests/site-isolation/now-playing-elected-across-frames.html
M LayoutTests/http/tests/site-isolation/now-playing-reelects-on-resize.html
A
LayoutTests/http/tests/site-isolation/now-playing-remote-command-no-owner-expected.txt
A
LayoutTests/http/tests/site-isolation/now-playing-remote-command-no-owner.html
A
LayoutTests/http/tests/site-isolation/now-playing-remote-command-routing-expected.txt
A
LayoutTests/http/tests/site-isolation/now-playing-remote-command-routing.html
M LayoutTests/http/tests/site-isolation/resources/now-playing-frame.html
A
LayoutTests/http/tests/site-isolation/resources/now-playing-test-helpers.js
M LayoutTests/media/utilities.js
M Source/WebCore/platform/MediaSessionIdentifier.h
M Source/WebCore/platform/MediaStrategy.cpp
M Source/WebCore/platform/MediaStrategy.h
M Source/WebCore/platform/audio/MediaSessionManagerInterface.cpp
M Source/WebCore/platform/audio/MediaSessionManagerInterface.h
M Source/WebCore/testing/Internals.cpp
M Source/WebCore/testing/Internals.h
M Source/WebCore/testing/Internals.idl
M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp
M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.h
M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.messages.in
M Source/WebKit/GPUProcess/GPUProcess.cpp
M Source/WebKit/GPUProcess/GPUProcess.h
M Source/WebKit/GPUProcess/GPUProcess.messages.in
M Source/WebKit/GPUProcess/GPUProcessCreationParameters.h
M Source/WebKit/GPUProcess/GPUProcessCreationParameters.serialization.in
M Source/WebKit/Scripts/webkit/messages.py
M Source/WebKit/Shared/ProcessQualified.serialization.in
M Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp
M Source/WebKit/UIProcess/Media/RemoteMediaSessionManagerProxy.cpp
M Source/WebKit/UIProcess/Media/RemoteMediaSessionManagerProxy.h
M Source/WebKit/UIProcess/Media/RemoteMediaSessionProxy.cpp
M Source/WebKit/UIProcess/Media/RemoteMediaSessionProxy.h
M Source/WebKit/WebProcess/GPU/GPUProcessConnection.cpp
M Source/WebKit/WebProcess/GPU/GPUProcessConnection.h
M Source/WebKit/WebProcess/GPU/GPUProcessConnection.messages.in
M Source/WebKit/WebProcess/GPU/media/WebMediaStrategy.cpp
M Source/WebKit/WebProcess/GPU/media/WebMediaStrategy.h
M Source/WebKit/WebProcess/WebPage/WebPage.cpp
M Source/WebKit/WebProcess/WebPage/WebPage.h
M Source/WebKit/WebProcess/WebProcess.cpp
M Source/WebKit/WebProcess/WebProcess.h
Log Message:
-----------
[Site Isolation] Deliver a received remote-control command to the same
session with and without site isolation
https://bugs.webkit.org/show_bug.cgi?id=322299
rdar://185537144
Reviewed by Jean-Yves Avenard.
The GPU process selects the single system NowPlaying session across processes,
but a
remote-control command it receives from the system was still delivered by local
re-selection: GPUConnectionToWebProcess sent the command to the elected web
process,
WebProcess::didReceiveRemoteCommand handed it to every page, and each
MediaSessionManagerInterface ran its own inline loop over its sessions (first
NowPlaying-eligible session, else the first that could receive commands). That
reached
the right process but not necessarily the specific session the GPU elected, and
it could
deliver spuriously to a non-elected page sharing the elected process. And when
no session
was NowPlaying-eligible, the command was dropped entirely — unlike the
non-site-isolated
path, which falls back to the current session.
Make site-isolated delivery match the non-site-isolated behavior (best eligible
session, else the current session):
- Thread the elected session's identifier through the received-command path.
GPUConnectionToWebProcess::didReceiveRemoteControlCommand includes
GPUProcess::remoteCommandTargetSessionInProcess() in the
DidReceiveRemoteCommand message,
threaded through GPUProcessConnection, WebProcess, and WebPage into
MediaSessionManagerInterface::processDidReceiveRemoteControlCommand as an
optional
targetSession. When set, the manager delivers to the one session whose
mediaSessionIdentifier matches (and that can receive remote commands) rather
than
re-selecting locally; a manager that does not own it delivers to nobody. When
it is
not set (non-site-isolated NowPlaying) behavior is unchanged: best eligible
session,
then the first that can receive commands. The parameter defaults to nullopt so
existing callers are unaffected.
processDidReceiveRemoteControlCommand now returns whether a session took the
command, so
WebProcess::didReceiveRemoteCommand can stop at the first page that claims an
elected
session instead of fanning out to all of them. If no page claims it — the
elected session
was destroyed or stopped accepting commands between the election and
delivery, since
DidReceiveRemoteCommand is asynchronous, or it was elected without being
command-capable
(nowPlayingEligibleSession() does not consult
canReceiveRemoteControlCommands(), and
AudioContext::canReceiveRemoteControlCommands() is false outside
ENABLE(DOM_AUDIO_SESSION))
— the pass runs again with nullopt so the command is not dropped. That retry
is best
effort: page iteration order is not stable and each page has its own manager
under site
isolation, so there is no cross-page current-session order to follow in the
web process.
- Route the command even when no session is NowPlaying-eligible. The GPU
process cannot
know the cross-process current-session order, but the UI process does:
RemoteMediaSessionManagerProxy mirrors every process's session and, whenever
the
current session changes, reports the first command-capable one to the GPU
process via
SetNowPlayingFallbackSession. When the election has no eligible owner,
GPUProcess::recomputeNowPlayingOwner seats that session's connection as a
command-only
client (becomeRemoteCommandFallbackTarget: it becomes the NowPlayingManager
client so
commands reach it and the system command listener stays alive, but it is not
shown in
the NowPlaying panel). The eligible owner (m_activeNowPlayingOwner: panel,
audio,
now-playing-app registration) is kept separate from the remote-command target
(m_remoteCommandTarget: where a received command is delivered).
The fallback names an exact session rather than only its process. Only the UI
process
knows the app-wide current-session order, and a process can host several
pages that each
have their own manager, so letting the target process re-select would let a
page that is
not the current session claim the command — the same spurious delivery this
change
removes for the elected case. The retry above covers the cost, which is that
an exact
identifier can go stale in flight.
- GPUProcess::nowPlayingClientDidClose resigns the seat when a seated web
process goes
away. GPUConnectionToWebProcess::didClose used to delegate resigning to a
recompute, but
the closing connection is still in GPUProcess::m_webProcessConnections at
that point and
gone by the time the recompute from removeGPUConnectionToWebProcess runs, so
a resign
that looks the connection up by process identifier silently did nothing: the
NowPlayingManager kept a dead client, its remote-command listener stayed
registered, and
MediaSessionManagerCocoa::clearNowPlayingInfo() was never called, leaving a
stale system
NowPlaying panel. didClose now resigns directly, and the GPU process drops
any fallback
or command-target state naming the closing process before recomputing so the
closing
connection cannot be seated again as the command-only fallback.
- The fallback walk skips a session whose web process is gone instead of giving
up on it,
so a later qualifying session is still reported.
- setNowPlayingFallbackSession only recomputes when there is no eligible owner.
The
fallback is read only when the election finds no winner, and every change to
the
candidates recomputes on its own, so an owner means a new fallback cannot
change the
outcome.
- The fallback session is seeded into a new GPU process through
GPUProcessCreationParameters rather than a didFinishLaunching hook, so it
arrives before
any web process can connect and there is no "every GPU launch resets the
cache" invariant
to maintain.
To let a layout test drive the whole path the way a real system command arrives,
Internals::postSystemRemoteControlCommand injects the command at the GPU
process's
NowPlayingManager rather than calling the manager directly. It is separate from
postRemoteControlCommand (which injects into the local manager) because the GPU
injection is gated on AllowTestOnlyIPC — which the existing
media/remote-control-command
tests do not enable — and because WebKitLegacy has no GPU process. It asks
MediaStrategy::postNowPlayingRemoteControlCommandToGPUProcessForTesting to hand
the command
to the GPU process, and delivers through the local manager itself when there is
none
(WebKitLegacy and non-GPU WebKit).
Internals::elementIsRemoteCommandTargetInGPUProcess
reports whether the GPU process would route a received command to a given
element's
session, which — unlike elementIsActiveNowPlayingSessionInGPUProcess — is also
true for the
command-only fallback, so a test can wait for the seat instead of re-posting
the command
until it lands.
Drive-by: the guard on processDidReceiveRemoteControlCommand was #if
ENABLE(VIDEO) ||
ENABLE(audio); ENABLE(audio) is always false, so it now reads
ENABLE(WEB_AUDIO). This makes the
function dispatch commands to WebAudio sessions in a VIDEO-off/WEB_AUDIO-on
build.
* LayoutTests/http/tests/site-isolation/now-playing-elected-across-frames.html:
* LayoutTests/http/tests/site-isolation/now-playing-reelects-on-resize.html:
*
LayoutTests/http/tests/site-isolation/now-playing-remote-command-no-owner.html:
Added.
*
LayoutTests/http/tests/site-isolation/now-playing-remote-command-no-owner-expected.txt:
Added.
*
LayoutTests/http/tests/site-isolation/now-playing-remote-command-routing.html:
Added.
*
LayoutTests/http/tests/site-isolation/now-playing-remote-command-routing-expected.txt:
Added.
* LayoutTests/http/tests/site-isolation/resources/now-playing-test-helpers.js:
Added.
(waitForSubframeMessage.):
(waitForSubframeMessage):
(subframeIsActiveNowPlaying):
(subframeIsPaused):
* LayoutTests/http/tests/site-isolation/resources/now-playing-frame.html:
* LayoutTests/media/utilities.js:
(async waitUntil):
* Source/WebCore/platform/MediaSessionIdentifier.h:
* Source/WebCore/platform/MediaStrategy.cpp:
(WebCore::MediaStrategy::isRemoteCommandTargetSessionInGPUProcessForTesting):
* Source/WebCore/platform/MediaStrategy.h:
(WebCore::MediaStrategy::postNowPlayingRemoteControlCommandToGPUProcessForTesting):
* Source/WebCore/platform/audio/MediaSessionManagerInterface.cpp:
(WebCore::MediaSessionManagerInterface::processDidReceiveRemoteControlCommand):
* Source/WebCore/platform/audio/MediaSessionManagerInterface.h:
* Source/WebCore/testing/Internals.cpp:
(WebCore::remoteControlCommandForString):
(WebCore::Internals::postRemoteControlCommand):
(WebCore::Internals::postSystemRemoteControlCommand):
(WebCore::Internals::elementIsRemoteCommandTargetInGPUProcess):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:
* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp:
(WebKit::GPUConnectionToWebProcess::setNowPlayingInfoForPage):
(WebKit::GPUConnectionToWebProcess::becomeRemoteCommandFallbackTarget):
(WebKit::GPUConnectionToWebProcess::resignNowPlayingManagerClient):
(WebKit::GPUConnectionToWebProcess::nowPlayingClientDidClose):
(WebKit::GPUConnectionToWebProcess::isRemoteCommandTargetSessionForTesting):
(WebKit::GPUConnectionToWebProcess::postNowPlayingRemoteControlCommandForTesting):
(WebKit::GPUConnectionToWebProcess::didReceiveRemoteControlCommand):
(WebKit::GPUConnectionToWebProcess::didClose):
(WebKit::GPUConnectionToWebProcess::clearNowPlayingInfoForPage):
(WebKit::GPUConnectionToWebProcess::becomeNowPlayingOwner):
(WebKit::GPUConnectionToWebProcess::updateSupportedRemoteCommands):
(WebKit::GPUConnectionToWebProcess::resignNowPlayingOwner): Deleted.
* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.h:
* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.messages.in:
* Source/WebKit/GPUProcess/GPUProcess.cpp:
(WebKit::GPUProcess::initializeGPUProcess):
(WebKit::GPUProcess::recomputeNowPlayingOwner):
(WebKit::GPUProcess::setNowPlayingFallbackSession):
(WebKit::GPUProcess::nowPlayingClientDidClose):
(WebKit::GPUProcess::remoteCommandTargetSessionInProcess const):
* Source/WebKit/GPUProcess/GPUProcess.h:
* Source/WebKit/GPUProcess/GPUProcess.messages.in:
* Source/WebKit/GPUProcess/GPUProcessCreationParameters.h:
* Source/WebKit/GPUProcess/GPUProcessCreationParameters.serialization.in:
* Source/WebKit/Scripts/webkit/messages.py:
(types_that_cannot_be_forward_declared):
(headers_for_type):
* Source/WebKit/Shared/ProcessQualified.serialization.in:
* Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp:
(WebKit::GPUProcessProxy::GPUProcessProxy):
* Source/WebKit/UIProcess/Media/RemoteMediaSessionManagerProxy.cpp:
(WebKit::RemoteMediaSessionManagerProxy::addMediaSession):
(WebKit::RemoteMediaSessionManagerProxy::removeMediaSession):
(WebKit::RemoteMediaSessionManagerProxy::webProcessWillShutDown):
(WebKit::RemoteMediaSessionManagerProxy::updateMediaSessionStates):
(WebKit::RemoteMediaSessionManagerProxy::mediaSessionStateChanged):
(WebKit::RemoteMediaSessionManagerProxy::setCurrentSession):
(WebKit::RemoteMediaSessionManagerProxy::computeNowPlayingFallbackSession
const):
(WebKit::RemoteMediaSessionManagerProxy::updateNowPlayingFallbackSession):
* Source/WebKit/UIProcess/Media/RemoteMediaSessionManagerProxy.h:
* Source/WebKit/UIProcess/Media/RemoteMediaSessionProxy.cpp:
(WebKit::RemoteMediaSessionProxy::qualifiedSessionIdentifier const):
* Source/WebKit/UIProcess/Media/RemoteMediaSessionProxy.h:
* Source/WebKit/WebProcess/GPU/GPUProcessConnection.cpp:
(WebKit::GPUProcessConnection::didReceiveRemoteCommand):
* Source/WebKit/WebProcess/GPU/GPUProcessConnection.h:
* Source/WebKit/WebProcess/GPU/GPUProcessConnection.messages.in:
* Source/WebKit/WebProcess/GPU/media/WebMediaStrategy.cpp:
(WebKit::WebMediaStrategy::isRemoteCommandTargetSessionInGPUProcessForTesting):
(WebKit::WebMediaStrategy::postNowPlayingRemoteControlCommandToGPUProcessForTesting):
* Source/WebKit/WebProcess/GPU/media/WebMediaStrategy.h:
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::didReceiveRemoteCommand):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::didReceiveRemoteCommand):
* Source/WebKit/WebProcess/WebProcess.h:
Canonical link: https://commits.webkit.org/320113@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications