Skip to content

Commit

Permalink
Use an ephemeral device ID for the Continuity camera when it is not t…
Browse files Browse the repository at this point in the history
…he system preferred

https://bugs.webkit.org/show_bug.cgi?id=247208
rdar://100335624

Reviewed by Jer Noble.

A "Continuity Camera", an iPhone used as a camera for a Macintosh, is listed as an
AVCaptureDevice whenever it is in close enough to the Macintosh to be used, but it is only
the "system preferred camera" when it is in an appropriate position and orientation to be
used as a camera for the device. This means that a script may remember the device ID when
a user has set up the phone to be used as a camera, and then pass it to getUserMedia later
when the phone is near the Macintosh but not usable as a camera - e.g. in the user's pocket.

To prevent this problem, use a device ID that is only valid for the lifetime of the
current frame when a Continuity Camera is available but is not the system preferred camera.

* LayoutTests/http/tests/media/media-stream/enumerate-devices-ephemeral-id-expected.txt: Added.
* LayoutTests/http/tests/media/media-stream/enumerate-devices-ephemeral-id.html: Added.
* LayoutTests/http/tests/media/media-stream/resources/enumerate-devices-ephemeral-id-iframe.html: Added.

* Source/WTF/wtf/PlatformHave.h: Define HAVE_CONTINUITY_CAMEARA

* Source/WebCore/Modules/mediastream/CanvasCaptureMediaStreamTrack.cpp:
(WebCore::CanvasCaptureMediaStreamTrack::Source::Source): Update for RealtimeMediaSource constructor
changes.
* Source/WebCore/Modules/mediastream/MediaDevices.cpp:
(WebCore::MediaDevices::exposeDevices): Take a MediaDeviceHashSalts instead of a String
because there are now two hash salts.
(WebCore::MediaDevices::enumerateDevices): Ditto.
* Source/WebCore/Modules/mediastream/MediaDevices.h:

* Source/WebCore/Modules/mediastream/UserMediaClient.h: Ditto.
* Source/WebCore/Modules/mediastream/UserMediaController.cpp:
* Source/WebCore/Modules/mediastream/UserMediaController.h:
(WebCore::UserMediaController::enumerateMediaDevices): Ditto.

* Source/WebCore/Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::allow): Ditto.
* Source/WebCore/Modules/mediastream/UserMediaRequest.h:

* Source/WebCore/Modules/speech/SpeechRecognitionCaptureSource.cpp:
(WebCore::SpeechRecognitionCaptureSource::createRealtimeMediaSource): Update for
RealtimeMediaSource constructor changes.

* Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp:
(WebCore::MediaStreamAudioSource::MediaStreamAudioSource): Ditto.

* Source/WebCore/WebCore.xcodeproj/project.pbxproj:

* Source/WebCore/platform/mediastream/CaptureDevice.h:
(WebCore::CaptureDevice::CaptureDevice): Add `isEphemeral` parameter.
(WebCore::CaptureDevice::setPersistentId):
(WebCore::CaptureDevice::setLabel):
(WebCore::CaptureDevice::isEphemeral const):
(WebCore::CaptureDevice::setIsEphemeral):
(WebCore::CaptureDevice::encode const):
(WebCore::CaptureDevice::decode):
(WebCore::CaptureDevice::isolatedCopy):

* Source/WebCore/platform/mediastream/RealtimeIncomingAudioSource.cpp:
(WebCore::RealtimeIncomingAudioSource::RealtimeIncomingAudioSource): Update for
RealtimeMediaSource constructor changes.

* Source/WebCore/platform/mediastream/RealtimeIncomingVideoSource.cpp:
(WebCore::RealtimeIncomingVideoSource::RealtimeIncomingVideoSource): Ditto.

* Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::toSourceType):
(WebCore::RealtimeMediaSource::RealtimeMediaSource): Take a CaptureDevice instead of a
type, name, and device ID. Take a MediaDeviceHashSalts instead of a String for the salts.
(WebCore::RealtimeMediaSource::setPersistentId):
(WebCore::RealtimeMediaSource::initializePersistentId): Calculate persistent and ephemeral
hashed IDs.
(WebCore::RealtimeMediaSource::fitnessDistance): m_hashedID -> hashedID()
(WebCore::RealtimeMediaSource::hashedId const): Consider ephemeral state.
(WebCore::RealtimeMediaSource::deviceIDHashSalts const):
(WebCore::RealtimeMediaSource::setLogger):
(WebCore::RealtimeMediaSource::deviceIDHashSalt const): Deleted.
* Source/WebCore/platform/mediastream/RealtimeMediaSource.h:

* Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.cpp:
(WebCore::RealtimeMediaSourceCenter::createMediaStream): Take MediaDeviceHashSalts with the
hash salts.
(WebCore::RealtimeMediaSourceCenter::getDisplayMediaDevices): Ditto.
(WebCore::RealtimeMediaSourceCenter::getUserMediaDevices): Ditto.
(WebCore::RealtimeMediaSourceCenter::validateRequestConstraints): Ditto.
(WebCore::RealtimeMediaSourceCenter::validateRequestConstraintsAfterEnumeration): Ditto.
* Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h:

* Source/WebCore/platform/mediastream/RealtimeMediaSourceFactory.h: Ditto.
* Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.h: Ditto.

* Source/WebCore/platform/mediastream/RealtimeVideoCaptureSource.cpp:
(WebCore::RealtimeVideoCaptureSource::RealtimeVideoCaptureSource): Update for RealtimeMediaSource
constructor changes.
* Source/WebCore/platform/mediastream/RealtimeVideoCaptureSource.h:

* Source/WebCore/platform/mediastream/RealtimeVideoSource.cpp:
(WebCore::RealtimeVideoSource::RealtimeVideoSource): Ditto.

* Source/WebCore/platform/mediastream/cocoa/DisplayCaptureSourceCocoa.cpp:
(WebCore::DisplayCaptureSourceCocoa::create): Ditto.
(WebCore::DisplayCaptureSourceCocoa::DisplayCaptureSourceCocoa): Ditto.
* Source/WebCore/platform/mediastream/cocoa/DisplayCaptureSourceCocoa.h:

* Source/WebCore/platform/mediastream/ios/CoreAudioCaptureSourceIOS.h:
* Source/WebCore/platform/mediastream/ios/CoreAudioCaptureSourceIOS.mm:
(WebCore::CoreAudioCaptureSourceFactoryIOS::createAudioCaptureSource): Ditto.

* Source/WebCore/platform/mediastream/mac/AVCaptureDeviceManager.mm:
(WebCore::AVCaptureDeviceManager::updateCachedAVCaptureDevices):
(WebCore::toCaptureDevice): Make Continuity Camera ephemeral when it is not the system
preferred camera.
(WebCore::AVCaptureDeviceManager::retrieveCaptureDevices):

* Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h:
* Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoCaptureSource::create): Update for RealtimeMediaSource constructor changes.
(WebCore::AVVideoCaptureSource::AVVideoCaptureSource): Ditto.

* Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp:
(WebCore::CoreAudioCaptureSource::create): Ditto.
(WebCore::CoreAudioCaptureSource::createForTesting): Ditto.
(WebCore::CoreAudioCaptureSource::CoreAudioCaptureSource): Ditto.
* Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h:
(WebCore::CoreAudioCaptureSourceFactory::createAudioCaptureSource):

* Source/WebCore/platform/mediastream/mac/MockAudioSharedUnit.mm:
(WebCore::MockRealtimeAudioSource::create): Ditto.

* Source/WebCore/platform/mediastream/mac/MockRealtimeVideoSourceMac.h:
* Source/WebCore/platform/mediastream/mac/MockRealtimeVideoSourceMac.mm:
(WebCore::MockRealtimeVideoSource::create): Ditto.
(WebCore::MockRealtimeVideoSourceMac::createForMockDisplayCapturer): Ditto.
(WebCore::MockRealtimeVideoSourceMac::MockRealtimeVideoSourceMac): Ditto.

* Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp:
* Source/WebCore/platform/mock/MockMediaDevice.h:
(WebCore::MockMediaDevice::captureDevice const): Allow to be ephemeral .
(WebCore::MockMediaDevice::encode const):
(WebCore::MockMediaDevice::decodeMockMediaDevice):
(WebCore::MockMediaDevice::decode):

* Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp:
(WebCore::MockRealtimeAudioSource::create): Update for RealtimeMediaSource constructor changes.
(WebCore::MockRealtimeAudioSource::MockRealtimeAudioSource): Ditto.
* Source/WebCore/platform/mock/MockRealtimeAudioSource.h:

* Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp:
(WebCore::defaultDevices): No devices are ephemeral by default.
(WebCore::MockDisplayCapturer::MockDisplayCapturer): Update for RealtimeMediaSource constructor
changes.
(WebCore::MockRealtimeMediaSourceCenter::setDeviceIsEphemeral):
* Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h:

* Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp:
(WebCore::MockRealtimeVideoSource::create): Ditto.
(WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource): Ditto.
* Source/WebCore/platform/mock/MockRealtimeVideoSource.h:

* Source/WebKit/GPUProcess/GPUProcess.cpp:
(WebKit::GPUProcess::setMockMediaDeviceIsEphemeral): Added for testing.
* Source/WebKit/GPUProcess/GPUProcess.h:
* Source/WebKit/GPUProcess/GPUProcess.messages.in:
* Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp:
(WKAddMockMediaDevice):
(WKSetMockMediaDeviceIsEphemeral):
* Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h:

* Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp:
(WebKit::UserMediaCaptureManagerProxy::createMicrophoneSource):
(WebKit::UserMediaCaptureManagerProxy::createCameraSource):
(WebKit::UserMediaCaptureManagerProxy::createMediaSourceForCaptureDeviceWithConstraints):
* Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.h:
* Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.messages.in:
* Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp:
(WebKit::GPUProcessProxy::setMockMediaDeviceIsEphemeral):
* Source/WebKit/UIProcess/GPU/GPUProcessProxy.h:
* Source/WebKit/UIProcess/SpeechRecognitionRemoteRealtimeMediaSource.cpp:
(WebKit::SpeechRecognitionRemoteRealtimeMediaSource::SpeechRecognitionRemoteRealtimeMediaSource):
(): Deleted.
* Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
(WebKit::UserMediaPermissionRequestManagerProxy::finishGrantingRequest): Take MediaDeviceHashSalts
for hash salts.
(WebKit::UserMediaPermissionRequestManagerProxy::didCommitLoadForFrame): Clear ephemeral
salt for a frame.
(WebKit::UserMediaPermissionRequestManagerProxy::resetAccess): Ditto.
(WebKit::UserMediaPermissionRequestManagerProxy::ephemeralDeviceHashSaltForFrame): Calculate
or lookup an ephemeral salt for a frame.
(WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionRequest): Use
MediaDeviceHashSalts.
(WebKit::UserMediaPermissionRequestManagerProxy::platformValidateUserMediaRequestConstraints): Ditto.
(WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionValidRequest): Ditto.
(WebKit::UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame): Ditto.
* Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h:

* Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.h:
(WebKit::UserMediaPermissionRequestProxy::setDeviceIdentifierHashSalts): *HashSalt -> *HashSalts
(WebKit::UserMediaPermissionRequestProxy::deviceIdentifierHashSalts const): Ditto
(WebKit::UserMediaPermissionRequestProxy::setDeviceIdentifierHashSalt): Deleted.
(WebKit::UserMediaPermissionRequestProxy::deviceIdentifierHashSalt const): Deleted.

* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didCommitLoadForFrame): Let UserMediaPermissionRequestManager know the
frame has reloaded so it can clear the ephemeral salt ID.
(WebKit::WebPageProxy::enumerateMediaDevicesForFrame):
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxy.messages.in:

* Source/WebKit/UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::setMockMediaDeviceIsEphemeral):
* Source/WebKit/UIProcess/WebProcessPool.h:

* Source/WebKit/WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp:
(WebKit::UserMediaPermissionRequestManager::userMediaAccessWasGranted):
(WebKit::UserMediaPermissionRequestManager::enumerateMediaDevices):
* Source/WebKit/WebProcess/MediaStream/UserMediaPermissionRequestManager.h:

* Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.cpp:
(WebKit::WebUserMediaClient::enumerateMediaDevices):
* Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.h:

* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::userMediaAccessWasGranted):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:

* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::setMockMediaDeviceIsEphemeral):
* Source/WebKit/WebProcess/WebProcess.h:
* Source/WebKit/WebProcess/WebProcess.messages.in:

* Source/WebKit/WebProcess/cocoa/RemoteRealtimeAudioSource.cpp:
(WebKit::RemoteRealtimeAudioSource::create): Update for RealtimeMediaSource constructor change.
(WebKit::RemoteRealtimeAudioSource::RemoteRealtimeAudioSource): Ditto.
* Source/WebKit/WebProcess/cocoa/RemoteRealtimeAudioSource.h:

* Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp:
(WebKit::RemoteRealtimeMediaSource::RemoteRealtimeMediaSource): Ditto.
(WebKit::RemoteRealtimeMediaSource::createRemoteMediaSource): Ditto.
(WebKit::toSourceType): Deleted.
(WebKit::m_manager): Deleted.
* Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h:

* Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSourceProxy.cpp:
(WebKit::RemoteRealtimeMediaSourceProxy::createRemoteMediaSource): Ditto.
* Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSourceProxy.h:

* Source/WebKit/WebProcess/cocoa/RemoteRealtimeVideoSource.cpp:
(WebKit::RemoteRealtimeVideoSource::create): Ditto.
(WebKit::RemoteRealtimeVideoSource::RemoteRealtimeVideoSource): Ditto.
(WebKit::RemoteRealtimeVideoSource::clone): Ditto.
* Source/WebKit/WebProcess/cocoa/RemoteRealtimeVideoSource.h:

* Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp:
(WebKit::UserMediaCaptureManager::AudioFactory::createAudioCaptureSource): Ditto.
(WebKit::UserMediaCaptureManager::VideoFactory::createVideoCaptureSource): Ditto.
(WebKit::UserMediaCaptureManager::DisplayFactory::createDisplayCaptureSource): Ditto.
* Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h:

* Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl: Add setMockMediaDeviceIsEphemeral.
* Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setMockMediaDeviceIsEphemeral):
* Tools/WebKitTestRunner/InjectedBundle/TestRunner.h:

* Tools/WebKitTestRunner/TestController.cpp:
(WTR::TestController::setMockMediaDeviceIsEphemeral):
* Tools/WebKitTestRunner/TestController.h:

* Tools/WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

Canonical link: https://commits.webkit.org/256161@main
  • Loading branch information
eric-carlson committed Oct 31, 2022
1 parent fdf21de commit 0ee91ab
Show file tree
Hide file tree
Showing 112 changed files with 722 additions and 365 deletions.
@@ -0,0 +1,4 @@


PASS Ephemeral device IDs

@@ -0,0 +1,72 @@
<!doctype html>
<html>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<body>
<iframe id=iframe allow="camera;microphone"></iframe>
<script>

let firstMessage = true;

async function sendMessage(isEphemeral, testName)
{
let handle;
const promise = new Promise((resolve, reject) => {
window.onmessage = resolve;
handle = setTimeout(() => reject(`${testName} test timed out`), 5000);
});

const iframe = document.getElementById('iframe');
const message = {
'setup': firstMessage,
'cameraId': 'built-in-camera',
'cameraName': 'Extra Fancy Camera',
'isEphemeral': isEphemeral
};
firstMessage = false;
iframe.contentWindow.postMessage(message, '*');

const event = await promise;
clearTimeout(handle);
return JSON.parse(event.data);
}

function loadIframe(url)
{
const iframe = document.getElementById('iframe');
iframe.src = url;
return new Promise(resolve => iframe.onload = resolve);
}

promise_test(async () => {

const iframeURL = 'resources/enumerate-devices-ephemeral-id-iframe.html';
await loadIframe(iframeURL);

// Ensure camera id is stable when called twice
let cameraState1 = await sendMessage(false, 'First non-ephemeral');
let cameraState2 = await sendMessage(false, 'Second non-ephemeral');
assert_equals(cameraState1.deviceId, cameraState2.deviceId, 'Same persistent id returned in two calls');

let ephemeralCameraState1 = await sendMessage(true, 'First ephemeral');
let ephemeralCameraState2 = await sendMessage(true, 'Second ephemeral');
assert_equals(ephemeralCameraState1.deviceId, ephemeralCameraState2.deviceId, 'Same ephemeral id returned in two calls');
assert_not_equals(cameraState1.deviceId, ephemeralCameraState1.deviceId, 'Ephemeral id not equal persistent id');

const iframe = document.getElementById('iframe');
const promise = new Promise(resolve => iframe.onload = resolve);
iframe.contentWindow.location.reload();
await promise;

let cameraState3 = await sendMessage(false, 'First non-ephemeral after reload');
assert_equals(cameraState3.deviceId, cameraState1.deviceId, 'Persistent id is the same after reload');

let ephemeralCameraState3 = await sendMessage(true, 'First ephemeral after reload');
assert_not_equals(ephemeralCameraState1.deviceId, ephemeralCameraState3.deviceId, 'Ephemeral id changed after reload');
assert_not_equals(cameraState1.deviceId, ephemeralCameraState3.deviceId, 'Ephemeral id after reload is not equal to persistent id');

}, 'Ephemeral device IDs');

</script>
</body>
</html>
@@ -0,0 +1,29 @@
<script>
async function enumerate(event)
{
if (event.data.setup && window.testRunner) {
testRunner.setUserMediaPermission(true);
testRunner.clearMockMediaDevices();
testRunner.addMockCameraDevice(event.data.cameraId, event.data.cameraName);
}

await navigator.mediaDevices.getUserMedia({ video: true });

testRunner.setMockMediaDeviceIsEphemeral(event.data.cameraId, event.data.isEphemeral);

const devices = await navigator.mediaDevices.enumerateDevices();
let result;
devices.forEach(device => {
if (device.label == event.data.cameraName)
result = device;
});

parent.postMessage(JSON.stringify(result), '*');
}

window.addEventListener('message', enumerate);
</script>

<div id='result'></div>


5 changes: 5 additions & 0 deletions Source/WTF/wtf/PlatformHave.h
Expand Up @@ -1342,3 +1342,8 @@
#define HAVE_UI_WINDOW_SCENE_LIVE_RESIZE 1
#endif
#endif

#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000) \
|| ((PLATFORM(IOS) || PLATFORM(MACCATALYST)) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 160000)
#define HAVE_CONTINUITY_CAMEARA 1
#endif
1 change: 1 addition & 0 deletions Source/WebCore/Headers.cmake
Expand Up @@ -281,6 +281,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
Modules/mediastream/DetachedRTCDataChannel.h
Modules/mediastream/DoubleRange.h
Modules/mediastream/LongRange.h
Modules/mediastream/MediaDeviceHashSalts.h
Modules/mediastream/MediaStreamTrack.h
Modules/mediastream/MediaTrackConstraints.h
Modules/mediastream/RTCController.h
Expand Down
Expand Up @@ -77,7 +77,7 @@ const char* CanvasCaptureMediaStreamTrack::activeDOMObjectName() const

// FIXME: Give source id and name
CanvasCaptureMediaStreamTrack::Source::Source(HTMLCanvasElement& canvas, std::optional<double>&& frameRequestRate)
: RealtimeMediaSource(Type::Video, "CanvasCaptureMediaStreamTrack"_s)
: RealtimeMediaSource(CaptureDevice { { }, CaptureDevice::DeviceType::Camera, "CanvasCaptureMediaStreamTrack"_s })
, m_frameRequestRate(WTFMove(frameRequestRate))
, m_requestFrameTimer(*this, &Source::requestFrameTimerFired)
, m_captureCanvasTimer(*this, &Source::captureCanvas)
Expand Down
60 changes: 60 additions & 0 deletions Source/WebCore/Modules/mediastream/MediaDeviceHashSalts.h
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 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. ``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
* 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

#include <wtf/HashFunctions.h>
#include <wtf/text/WTFString.h>

namespace WebCore {

struct MediaDeviceHashSalts {
String persistentDeviceSalt;
String ephemeralDeviceSalt;

template<class Encoder> void encode(Encoder&) const;
template<class Decoder> static WARN_UNUSED_RETURN bool decode(Decoder&, MediaDeviceHashSalts&);
};

template<class Encoder>
void MediaDeviceHashSalts::encode(Encoder& encoder) const
{
encoder << persistentDeviceSalt
<< ephemeralDeviceSalt;
}

template<class Decoder>
bool MediaDeviceHashSalts::decode(Decoder& decoder, MediaDeviceHashSalts& settings)
{
return decoder.decode(settings.persistentDeviceSalt)
&& decoder.decode(settings.ephemeralDeviceSalt);
}

} // namespace WebCore

namespace WTF {
template<> struct DefaultHash<WebCore::MediaDeviceHashSalts>;
template<> struct HashTraits<WebCore::MediaDeviceHashSalts>;
}
15 changes: 10 additions & 5 deletions Source/WebCore/Modules/mediastream/MediaDevices.cpp
Expand Up @@ -290,7 +290,7 @@ static inline MediaDeviceInfo::Kind toMediaDeviceInfoKind(CaptureDevice::DeviceT
return MediaDeviceInfo::Kind::Audioinput;
}

void MediaDevices::exposeDevices(const Vector<CaptureDevice>& newDevices, const String& deviceIDHashSalt, EnumerateDevicesPromise&& promise)
void MediaDevices::exposeDevices(const Vector<CaptureDevice>& newDevices, MediaDeviceHashSalts&& deviceIDHashSalts, EnumerateDevicesPromise&& promise)
{
if (isContextStopped())
return;
Expand All @@ -312,8 +312,13 @@ void MediaDevices::exposeDevices(const Vector<CaptureDevice>& newDevices, const
if (!canAccessSpeaker && newDevice.type() == CaptureDevice::DeviceType::Speaker)
continue;

auto deviceId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(newDevice.persistentId(), deviceIDHashSalt);
auto groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(newDevice.groupId(), m_groupIdHashSalt);
auto& center = RealtimeMediaSourceCenter::singleton();
String deviceId;
if (newDevice.isEphemeral())
deviceId = center.hashStringWithSalt(newDevice.persistentId(), deviceIDHashSalts.ephemeralDeviceSalt);
else
deviceId = center.hashStringWithSalt(newDevice.persistentId(), deviceIDHashSalts.persistentDeviceSalt);
auto groupId = center.hashStringWithSalt(newDevice.groupId(), m_groupIdHashSalt);

if (newDevice.type() == CaptureDevice::DeviceType::Speaker)
m_audioOutputDeviceIdToPersistentId.add(deviceId, newDevice.persistentId());
Expand Down Expand Up @@ -341,10 +346,10 @@ void MediaDevices::enumerateDevices(EnumerateDevicesPromise&& promise)
return;
}

controller->enumerateMediaDevices(*document, [this, weakThis = WeakPtr { *this }, promise = WTFMove(promise)](const auto& newDevices, const auto& deviceIDHashSalt) mutable {
controller->enumerateMediaDevices(*document, [this, weakThis = WeakPtr { *this }, promise = WTFMove(promise)](const auto& newDevices, MediaDeviceHashSalts&& deviceIDHashSalts) mutable {
if (!weakThis)
return;
exposeDevices(newDevices, deviceIDHashSalt, WTFMove(promise));
exposeDevices(newDevices, WTFMove(deviceIDHashSalts), WTFMove(promise));
});
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/mediastream/MediaDevices.h
Expand Up @@ -101,7 +101,7 @@ class MediaDevices final : public RefCounted<MediaDevices>, public ActiveDOMObje
void scheduledEventTimerFired();
bool addEventListener(const AtomString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;

void exposeDevices(const Vector<CaptureDevice>&, const String&, EnumerateDevicesPromise&&);
void exposeDevices(const Vector<CaptureDevice>&, MediaDeviceHashSalts&&, EnumerateDevicesPromise&&);
void listenForDeviceChanges();

friend class JSMediaDevicesOwner;
Expand Down
7 changes: 5 additions & 2 deletions Source/WebCore/Modules/mediastream/UserMediaClient.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
* Copyright (C) 2016-2018 Apple Inc. All rights reserved.
* Copyright (C) 2016-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -43,14 +43,17 @@ class Document;
class Page;
class UserMediaRequest;

struct MediaDeviceHashSalts;

class UserMediaClient {
public:
virtual void pageDestroyed() = 0;

virtual void requestUserMediaAccess(UserMediaRequest&) = 0;
virtual void cancelUserMediaAccessRequest(UserMediaRequest&) = 0;

virtual void enumerateMediaDevices(Document&, CompletionHandler<void(const Vector<CaptureDevice>&, const String&)>&&) = 0;
using EnumerateDevicesCallback = CompletionHandler<void(const Vector<CaptureDevice>&, MediaDeviceHashSalts&&)>;
virtual void enumerateMediaDevices(Document&, EnumerateDevicesCallback&&) = 0;

enum DeviceChangeObserverTokenType { };
using DeviceChangeObserverToken = ObjectIdentifier<DeviceChangeObserverTokenType>;
Expand Down
3 changes: 1 addition & 2 deletions Source/WebCore/Modules/mediastream/UserMediaController.cpp
Expand Up @@ -30,8 +30,7 @@

#include "DOMWindow.h"
#include "Document.h"
#include "Frame.h"
#include "HTMLIFrameElement.h"
#include "RealtimeMediaSourceCenter.h"
#include "UserMediaRequest.h"

namespace WebCore {
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/Modules/mediastream/UserMediaController.h
Expand Up @@ -47,7 +47,7 @@ class UserMediaController : public Supplement<Page> {
void requestUserMediaAccess(UserMediaRequest&);
void cancelUserMediaAccessRequest(UserMediaRequest&);

void enumerateMediaDevices(Document&, CompletionHandler<void(const Vector<CaptureDevice>&, const String&)>&&);
void enumerateMediaDevices(Document&, UserMediaClient::EnumerateDevicesCallback&&);

UserMediaClient::DeviceChangeObserverToken addDeviceChangeObserver(Function<void()>&&);
void removeDeviceChangeObserver(UserMediaClient::DeviceChangeObserverToken);
Expand All @@ -73,7 +73,7 @@ inline void UserMediaController::cancelUserMediaAccessRequest(UserMediaRequest&
m_client->cancelUserMediaAccessRequest(request);
}

inline void UserMediaController::enumerateMediaDevices(Document& document, CompletionHandler<void(const Vector<CaptureDevice>&, const String&)>&& completionHandler)
inline void UserMediaController::enumerateMediaDevices(Document& document, UserMediaClient::EnumerateDevicesCallback&& completionHandler)
{
m_client->enumerateMediaDevices(document, WTFMove(completionHandler));
}
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
Expand Up @@ -149,7 +149,7 @@ static inline bool isMediaStreamCorrectlyStarted(const MediaStream& stream)
});
}

void UserMediaRequest::allow(CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, String&& deviceIdentifierHashSalt, CompletionHandler<void()>&& completionHandler)
void UserMediaRequest::allow(CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, MediaDeviceHashSalts&& deviceIdentifierHashSalt, CompletionHandler<void()>&& completionHandler)
{
RELEASE_LOG(MediaStream, "UserMediaRequest::allow %s %s", audioDevice ? audioDevice.persistentId().utf8().data() : "", videoDevice ? videoDevice.persistentId().utf8().data() : "");
m_allowCompletionHandler = WTFMove(completionHandler);
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/mediastream/UserMediaRequest.h
Expand Up @@ -63,7 +63,7 @@ class UserMediaRequest : public RefCounted<UserMediaRequest>, public ActiveDOMOb
void start();

WEBCORE_EXPORT void setAllowedMediaDeviceUIDs(const String& audioDeviceUID, const String& videoDeviceUID);
WEBCORE_EXPORT void allow(CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, String&& deviceIdentifierHashSalt, CompletionHandler<void()>&&);
WEBCORE_EXPORT void allow(CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, MediaDeviceHashSalts&&, CompletionHandler<void()>&&);

enum MediaAccessDenialReason { NoConstraints, UserMediaDisabled, NoCaptureDevices, InvalidConstraint, HardwareError, PermissionDenied, InvalidAccess, OtherFailure };
WEBCORE_EXPORT void deny(MediaAccessDenialReason, const String& errorMessage = emptyString());
Expand Down
Expand Up @@ -64,7 +64,7 @@ std::optional<CaptureDevice> SpeechRecognitionCaptureSource::findCaptureDevice()

CaptureSourceOrError SpeechRecognitionCaptureSource::createRealtimeMediaSource(const CaptureDevice& captureDevice, PageIdentifier pageIdentifier)
{
return RealtimeMediaSourceCenter::singleton().audioCaptureFactory().createAudioCaptureSource(captureDevice, "SpeechID"_s, { }, pageIdentifier);
return RealtimeMediaSourceCenter::singleton().audioCaptureFactory().createAudioCaptureSource(captureDevice, { "SpeechID"_s, "SpeechID"_s }, { }, pageIdentifier);
}

SpeechRecognitionCaptureSource::SpeechRecognitionCaptureSource(SpeechRecognitionConnectionClientIdentifier clientIdentifier, DataCallback&& dataCallback, StateUpdateCallback&& stateUpdateCallback, Ref<RealtimeMediaSource>&& source)
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp
Expand Up @@ -34,7 +34,7 @@
namespace WebCore {

MediaStreamAudioSource::MediaStreamAudioSource(float sampleRate)
: RealtimeMediaSource(RealtimeMediaSource::Type::Audio, "MediaStreamAudioDestinationNode"_s)
: RealtimeMediaSource(CaptureDevice { { }, CaptureDevice::DeviceType::Microphone, "MediaStreamAudioDestinationNode"_s })
{
m_currentSettings.setSampleRate(sampleRate);
}
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Expand Up @@ -128,6 +128,7 @@
0754A5EA215EA3B8002D3A99 /* RealtimeMediaSourceFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 0754A5E8215EA3B7002D3A99 /* RealtimeMediaSourceFactory.h */; settings = {ATTRIBUTES = (Private, ); }; };
0757B13E214AE79900794B0D /* VideoPreset.h in Headers */ = {isa = PBXBuildFile; fileRef = 0757B13C214AE79700794B0D /* VideoPreset.h */; settings = {ATTRIBUTES = (Private, ); }; };
07611DC12440E59B00D80704 /* MediaUsageInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 07611DB8243FB75C00D80704 /* MediaUsageInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
07637267290330610055DDA2 /* MediaDeviceHashSalts.h in Headers */ = {isa = PBXBuildFile; fileRef = 0763726629030BB50055DDA2 /* MediaDeviceHashSalts.h */; settings = {ATTRIBUTES = (Private, ); }; };
07638A991884487200E15A1B /* MediaSessionManagerIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 07638A971884487200E15A1B /* MediaSessionManagerIOS.h */; settings = {ATTRIBUTES = (Private, ); }; };
07638A9A1884487200E15A1B /* MediaSessionManagerIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07638A981884487200E15A1B /* MediaSessionManagerIOS.mm */; };
076970871463AD8700F502CF /* TextTrackList.h in Headers */ = {isa = PBXBuildFile; fileRef = 076970851463AD8700F502CF /* TextTrackList.h */; };
Expand Down Expand Up @@ -6274,6 +6275,7 @@
0757B13C214AE79700794B0D /* VideoPreset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoPreset.h; sourceTree = "<group>"; };
075CC6C228EE99E100EAAF61 /* IconCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = IconCocoa.mm; sourceTree = "<group>"; };
07611DB8243FB75C00D80704 /* MediaUsageInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaUsageInfo.h; sourceTree = "<group>"; };
0763726629030BB50055DDA2 /* MediaDeviceHashSalts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaDeviceHashSalts.h; sourceTree = "<group>"; };
07638A971884487200E15A1B /* MediaSessionManagerIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSessionManagerIOS.h; sourceTree = "<group>"; };
07638A981884487200E15A1B /* MediaSessionManagerIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaSessionManagerIOS.mm; sourceTree = "<group>"; };
076970841463AD8700F502CF /* TextTrackList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextTrackList.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -19012,6 +19014,7 @@
93A806121E03B51C008A1F26 /* DoubleRange.idl */,
93A806131E03B51C008A1F26 /* LongRange.h */,
93A806141E03B51C008A1F26 /* LongRange.idl */,
0763726629030BB50055DDA2 /* MediaDeviceHashSalts.h */,
159AE82A1B3A402F0037478B /* MediaDeviceInfo.cpp */,
15145B8F1B3A1B3E00662BF7 /* MediaDeviceInfo.h */,
15145B911B3A1D4C00662BF7 /* MediaDeviceInfo.idl */,
Expand Down Expand Up @@ -37431,6 +37434,7 @@
CD3EEF3825799F73006563BB /* MediaDecodingConfiguration.h in Headers */,
CD3EEF3E25799FC7006563BB /* MediaDecodingType.h in Headers */,
CDF2B004181F059C00F2B424 /* MediaDescription.h in Headers */,
07637267290330610055DDA2 /* MediaDeviceHashSalts.h in Headers */,
15145B901B3A1CE000662BF7 /* MediaDeviceInfo.h in Headers */,
5EA725D31ACABD4700EAD17B /* MediaDevices.h in Headers */,
97205AB81239291000B17380 /* MediaDocument.h in Headers */,
Expand Down

0 comments on commit 0ee91ab

Please sign in to comment.