Skip to content

Commit

Permalink
getUserMedia camera stream lost on history pushState in iOS 17.4 Beta 4
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=269846
rdar://123381737

Reviewed by Eric Carlson and Youenn Fablet.

When WebKit adopted media capability grants for camera capture we chose to tie the lifetime of the
media environment to the top frame document's current URL, such that if the URL changes (ignoring
fragment identifiers) then the current media environment is destroyed and a new one is created. If
a capture session is active when the media environment changes then the system will pause the
capture session as it's no longer associated with the current media environment. The logic of
comparing URLs was meant as a proxy for detecting cross-document navigations but failed to account
for same-document navigations that changed the path of the current URL (e.g., via pushState). Since
the origin associated with the media environment does not change during a same-document navigation
there is no need to recreate the media environment.

Addressed this by moving the logic for creating and destroying media environments to
WebPageProxy::didChangeMainDocument. Further, since only the top document's origin is displayed to
the user in iOS's privacy accounting UI, changed MediaCapability to only track a SecurityOrigin.
The logic for when to activate and deactivate a media environment's capability remains unchanged.

Manually verified that this resolves the issue reported in bug #269846. Unfortunately no new tests
are possible since the underlying platform support for media capabilities is not available in
iOS Simulator.

* Source/WebKit/Platform/cocoa/MediaCapability.h:
* Source/WebKit/Platform/cocoa/MediaCapability.mm:
(WebKit::MediaCapability::MediaCapability):
(WebKit::m_mediaEnvironment):
(WebKit::MediaCapability::registrableDomain const): Deleted.
* Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm:
(WebKit::WebPageProxy::setMediaCapability):
(WebKit::WebPageProxy::deactivateMediaCapability):
(WebKit::WebPageProxy::resetMediaCapability):
(WebKit::WebPageProxy::updateMediaCapability):
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didCommitLoadForFrame):
(WebKit::WebPageProxy::didChangeMainDocument):
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:

Canonical link: https://commits.webkit.org/275244@main
  • Loading branch information
aestes committed Feb 23, 2024
1 parent e33391b commit d97f5a0
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 45 deletions.
11 changes: 5 additions & 6 deletions Source/WebKit/Platform/cocoa/MediaCapability.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
#include "ExtensionCapability.h"
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
#include <wtf/URL.h>
#include <wtf/Ref.h>
#include <wtf/WeakPtr.h>

namespace WebCore {
class RegistrableDomain;
class SecurityOrigin;
}

OBJC_CLASS BEMediaEnvironment;
Expand All @@ -46,7 +46,7 @@ class ExtensionCapabilityGrant;
class MediaCapability final : public ExtensionCapability, public CanMakeWeakPtr<MediaCapability> {
WTF_MAKE_NONCOPYABLE(MediaCapability);
public:
explicit MediaCapability(URL);
explicit MediaCapability(Ref<WebCore::SecurityOrigin>&&);
MediaCapability(MediaCapability&&) = default;
MediaCapability& operator=(MediaCapability&&) = default;

Expand All @@ -61,8 +61,7 @@ class MediaCapability final : public ExtensionCapability, public CanMakeWeakPtr<
void setState(State state) { m_state = state; }
bool isActivatingOrActive() const;

const URL& url() const { return m_url; }
WebCore::RegistrableDomain registrableDomain() const;
const WebCore::SecurityOrigin& securityOrigin() const { return m_securityOrigin.get(); }

// ExtensionCapability
String environmentIdentifier() const final;
Expand All @@ -71,7 +70,7 @@ class MediaCapability final : public ExtensionCapability, public CanMakeWeakPtr<

private:
State m_state { State::Inactive };
URL m_url;
Ref<WebCore::SecurityOrigin> m_securityOrigin;
RetainPtr<BEMediaEnvironment> m_mediaEnvironment;
};

Expand Down
15 changes: 4 additions & 11 deletions Source/WebKit/Platform/cocoa/MediaCapability.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,16 @@
#if ENABLE(EXTENSION_CAPABILITIES)

#import <BrowserEngineKit/BECapability.h>
#import <WebCore/RegistrableDomain.h>
#import <WebCore/SecurityOrigin.h>
#import <wtf/text/WTFString.h>

#import "ExtensionKitSoftLink.h"

namespace WebKit {

using WebCore::RegistrableDomain;

MediaCapability::MediaCapability(URL url)
: m_url { WTFMove(url) }
, m_mediaEnvironment(adoptNS([[BEMediaEnvironment alloc] initWithWebPageURL:m_url]))
MediaCapability::MediaCapability(Ref<WebCore::SecurityOrigin>&& securityOrigin)
: m_securityOrigin { WTFMove(securityOrigin) }
, m_mediaEnvironment { adoptNS([[BEMediaEnvironment alloc] initWithWebPageURL:m_securityOrigin->toURL()]) }
{
setPlatformCapability([BEProcessCapability mediaPlaybackAndCaptureWithEnvironment:m_mediaEnvironment.get()]);
}
Expand All @@ -60,11 +58,6 @@
return false;
}

RegistrableDomain MediaCapability::registrableDomain() const
{
return RegistrableDomain { m_url };
}

String MediaCapability::environmentIdentifier() const
{
#if USE(EXTENSIONKIT)
Expand Down
39 changes: 25 additions & 14 deletions Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1032,23 +1032,29 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t

void WebPageProxy::setMediaCapability(std::optional<MediaCapability>&& capability)
{
if (auto oldCapability = std::exchange(internals().mediaCapability, std::nullopt)) {
WEBPAGEPROXY_RELEASE_LOG(ProcessCapabilities, "setMediaCapability: deactivating (envID=%{public}s) for registrable domain '%{sensitive}s'", oldCapability->environmentIdentifier().utf8().data(), oldCapability->registrableDomain().string().utf8().data());
Ref processPool { protectedProcess()->protectedProcessPool() };
processPool->extensionCapabilityGranter().setMediaCapabilityActive(*oldCapability, false);
processPool->extensionCapabilityGranter().revoke(*oldCapability);
}
if (auto oldCapability = std::exchange(internals().mediaCapability, std::nullopt))
deactivateMediaCapability(*oldCapability);

internals().mediaCapability = WTFMove(capability);

if (auto& newCapability = internals().mediaCapability) {
WEBPAGEPROXY_RELEASE_LOG(ProcessCapabilities, "setMediaCapability: creating (envID=%{public}s) for registrable domain '%{sensitive}s'", newCapability->environmentIdentifier().utf8().data(), newCapability->registrableDomain().string().utf8().data());
send(Messages::WebPage::SetMediaEnvironment(newCapability->environmentIdentifier()));
} else
if (!internals().mediaCapability) {
send(Messages::WebPage::SetMediaEnvironment({ }));
return;
}

WEBPAGEPROXY_RELEASE_LOG(ProcessCapabilities, "setMediaCapability: creating (envID=%{public}s) for host '%{sensitive}s'", internals().mediaCapability->environmentIdentifier().utf8().data(), internals().mediaCapability->securityOrigin().host().utf8().data());
send(Messages::WebPage::SetMediaEnvironment(internals().mediaCapability->environmentIdentifier()));
}

void WebPageProxy::updateMediaCapability()
void WebPageProxy::deactivateMediaCapability(MediaCapability& capability)
{
WEBPAGEPROXY_RELEASE_LOG(ProcessCapabilities, "deactivateMediaCapability: deactivating (envID=%{public}s) for host '%{sensitive}s'", capability.environmentIdentifier().utf8().data(), capability.securityOrigin().host().utf8().data());
Ref processPool { protectedProcess()->protectedProcessPool() };
processPool->extensionCapabilityGranter().setMediaCapabilityActive(capability, false);
processPool->extensionCapabilityGranter().revoke(capability);
}

void WebPageProxy::resetMediaCapability()
{
if (!preferences().mediaCapabilityGrantsEnabled())
return;
Expand All @@ -1060,15 +1066,20 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t
return;
}

if (!mediaCapability() || !equalIgnoringFragmentIdentifier(mediaCapability()->url(), currentURL))
setMediaCapability(MediaCapability { currentURL });
Ref securityOrigin = SecurityOrigin::create(currentURL);

if (!mediaCapability() || !mediaCapability()->securityOrigin().isSameOriginAs(securityOrigin.get()))
setMediaCapability(MediaCapability { WTFMove(securityOrigin) });
}

void WebPageProxy::updateMediaCapability()
{
auto& mediaCapability = internals().mediaCapability;
if (!mediaCapability)
return;

if (shouldDeactivateMediaCapability()) {
setMediaCapability(std::nullopt);
deactivateMediaCapability(*mediaCapability);
return;
}

Expand Down
19 changes: 9 additions & 10 deletions Source/WebKit/UIProcess/WebPageProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6314,11 +6314,6 @@ void WebPageProxy::didCommitLoadForFrame(FrameIdentifier frameID, FrameInfoData&
if (m_userMediaPermissionRequestManager)
m_userMediaPermissionRequestManager->didCommitLoadForFrame(frameID);
#endif

#if ENABLE(EXTENSION_CAPABILITIES)
if (frame->isMainFrame())
updateMediaCapability();
#endif
}

void WebPageProxy::setCrossSiteLoadWithLinkDecorationForTesting(const URL& fromURL, const URL& toURL, bool wasFiltered, CompletionHandler<void()>&& completionHandler)
Expand Down Expand Up @@ -6594,25 +6589,29 @@ void WebPageProxy::didSameDocumentNavigationForFrameViaJSHistoryAPI(SameDocument

void WebPageProxy::didChangeMainDocument(FrameIdentifier frameID)
{
RefPtr frame = WebFrameProxy::webFrame(frameID);

#if ENABLE(MEDIA_STREAM)
if (m_userMediaPermissionRequestManager) {
m_userMediaPermissionRequestManager->resetAccess(frameID);

#if ENABLE(GPU_PROCESS)
if (RefPtr gpuProcess = m_process->processPool().gpuProcess()) {
if (RefPtr frame = WebFrameProxy::webFrame(frameID))
if (frame)
gpuProcess->updateCaptureOrigin(SecurityOriginData::fromURLWithoutStrictOpaqueness(frame->url()), m_process->coreProcessIdentifier());
}
#endif
}

#else
UNUSED_PARAM(frameID);
#endif

m_isQuotaIncreaseDenied = false;

m_speechRecognitionPermissionManager = nullptr;

#if ENABLE(EXTENSION_CAPABILITIES)
if (frame && frame->isMainFrame())
resetMediaCapability();
#endif
}

#if ENABLE(GPU_PROCESS)
Expand Down
2 changes: 2 additions & 0 deletions Source/WebKit/UIProcess/WebPageProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ class WebPageProxy final : public API::ObjectImpl<API::Object::Type::Page>, publ

#if ENABLE(EXTENSION_CAPABILITIES)
const std::optional<MediaCapability>& mediaCapability() const;
void resetMediaCapability();
void updateMediaCapability();
#endif
void setCrossSiteLoadWithLinkDecorationForTesting(const URL& fromURL, const URL& toURL, bool wasFiltered, CompletionHandler<void()>&&);
Expand Down Expand Up @@ -2976,6 +2977,7 @@ class WebPageProxy final : public API::ObjectImpl<API::Object::Type::Page>, publ

#if ENABLE(EXTENSION_CAPABILITIES)
void setMediaCapability(std::optional<MediaCapability>&&);
void deactivateMediaCapability(MediaCapability&);
bool shouldActivateMediaCapability() const;
bool shouldDeactivateMediaCapability() const;
#endif
Expand Down
16 changes: 12 additions & 4 deletions Source/WebKit/WebKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19725,7 +19725,9 @@
};
5C400E6A29DB8AB500446F6F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
platformFilters = (
ios,
);
target = 5C139DA129DB82E500D5117B /* WebContentCaptivePortalExtension */;
targetProxy = 5C400E6929DB8AB500446F6F /* PBXContainerItemProxy */;
};
Expand All @@ -19741,19 +19743,25 @@
};
5CE4B60729CEBB760038F565 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
platformFilters = (
ios,
);
target = 5CA5A2C929CBBA1A000F1046 /* WebContentExtension */;
targetProxy = 5CE4B60629CEBB760038F565 /* PBXContainerItemProxy */;
};
5CE4B62329CF880B0038F565 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
platformFilters = (
ios,
);
target = 5CE4B61529CF877F0038F565 /* GPUExtension */;
targetProxy = 5CE4B62229CF880B0038F565 /* PBXContainerItemProxy */;
};
5CE4B62529CF880B0038F565 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
platformFilters = (
ios,
);
target = 5CE4B60829CF87680038F565 /* NetworkingExtension */;
targetProxy = 5CE4B62429CF880B0038F565 /* PBXContainerItemProxy */;
};
Expand Down

0 comments on commit d97f5a0

Please sign in to comment.