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

Reply via email to