blob: a875534c25c4bb779f2da869cf0d0bcb032c1487 [file] [edit]
/*
* Copyright (C) 2014 Igalia S.L.
* Copyright (C) 2016 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "UserMediaPermissionRequestManagerProxy.h"
#include "APISecurityOrigin.h"
#include "APIUIClient.h"
#include "WebPageMessages.h"
#include "WebPageProxy.h"
#include "WebProcessProxy.h"
#include <WebCore/MediaConstraintsImpl.h>
#include <WebCore/MockRealtimeMediaSourceCenter.h>
#include <WebCore/RealtimeMediaSource.h>
using namespace WebCore;
namespace WebKit {
UserMediaPermissionRequestManagerProxy::UserMediaPermissionRequestManagerProxy(WebPageProxy& page)
: m_page(page)
{
}
void UserMediaPermissionRequestManagerProxy::invalidateRequests()
{
for (auto& request : m_pendingUserMediaRequests.values())
request->invalidate();
m_pendingUserMediaRequests.clear();
for (auto& request : m_pendingDeviceRequests.values())
request->invalidate();
m_pendingDeviceRequests.clear();
m_pageSandboxExtensionsGranted.clear();
}
Ref<UserMediaPermissionRequestProxy> UserMediaPermissionRequestManagerProxy::createRequest(uint64_t userMediaID, const Vector<String>& audioDeviceUIDs, const Vector<String>& videoDeviceUIDs)
{
Ref<UserMediaPermissionRequestProxy> request = UserMediaPermissionRequestProxy::create(*this, userMediaID, audioDeviceUIDs, videoDeviceUIDs);
m_pendingUserMediaRequests.add(userMediaID, request.ptr());
return request;
}
Ref<UserMediaPermissionRequestProxy> UserMediaPermissionRequestManagerProxy::createRequest(uint64_t userMediaID)
{
Ref<UserMediaPermissionRequestProxy> request = UserMediaPermissionRequestProxy::create(*this, userMediaID, Vector<String>(), Vector<String>());
m_pendingUserMediaRequests.add(userMediaID, request.ptr());
return request;
}
#if ENABLE(MEDIA_STREAM)
static uint64_t toWebCore(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason)
{
switch (reason) {
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::NoConstraints);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::UserMediaDisabled);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoCaptureDevices:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::NoCaptureDevices);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::InvalidConstraint);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::HardwareError:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::HardwareError);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::PermissionDenied);
break;
case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure:
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::OtherFailure);
break;
}
ASSERT_NOT_REACHED();
return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::OtherFailure);
}
#endif
void UserMediaPermissionRequestManagerProxy::userMediaAccessWasDenied(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason)
{
if (!m_page.isValid())
return;
if (!m_pendingUserMediaRequests.take(userMediaID))
return;
denyRequest(userMediaID, reason, "");
}
void UserMediaPermissionRequestManagerProxy::denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason, const String& invalidConstraint)
{
ASSERT(m_page.isValid());
#if ENABLE(MEDIA_STREAM)
m_page.process().send(Messages::WebPage::UserMediaAccessWasDenied(userMediaID, toWebCore(reason), invalidConstraint), m_page.pageID());
#else
UNUSED_PARAM(reason);
UNUSED_PARAM(invalidConstraint);
#endif
}
void UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted(uint64_t userMediaID, const String& audioDeviceUID, const String& videoDeviceUID)
{
ASSERT(!audioDeviceUID.isEmpty() || !videoDeviceUID.isEmpty());
if (!m_page.isValid())
return;
if (!m_pendingUserMediaRequests.take(userMediaID))
return;
#if ENABLE(MEDIA_STREAM)
size_t extensionCount = 0;
unsigned requiredExtensions = SandboxExtensionsGranted::None;
if (!audioDeviceUID.isEmpty()) {
requiredExtensions |= SandboxExtensionsGranted::Audio;
extensionCount++;
}
if (!videoDeviceUID.isEmpty()) {
requiredExtensions |= SandboxExtensionsGranted::Video;
extensionCount++;
}
unsigned currentExtensions = m_pageSandboxExtensionsGranted.get(m_page.pageID());
if (!(requiredExtensions & currentExtensions)) {
ASSERT(extensionCount);
m_pageSandboxExtensionsGranted.set(m_page.pageID(), requiredExtensions | currentExtensions);
SandboxExtension::HandleArray handles;
handles.allocate(extensionCount);
if (!videoDeviceUID.isEmpty())
SandboxExtension::createHandleForGenericExtension("com.apple.webkit.camera", handles[--extensionCount]);
if (!audioDeviceUID.isEmpty())
SandboxExtension::createHandleForGenericExtension("com.apple.webkit.microphone", handles[--extensionCount]);
m_page.process().send(Messages::WebPage::GrantUserMediaDevicesSandboxExtension(handles), m_page.pageID());
}
m_page.process().send(Messages::WebPage::UserMediaAccessWasGranted(userMediaID, audioDeviceUID, videoDeviceUID), m_page.pageID());
#else
UNUSED_PARAM(audioDeviceUID);
UNUSED_PARAM(videoDeviceUID);
#endif
}
void UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier, const WebCore::MediaConstraintsData& audioConstraintsData, const WebCore::MediaConstraintsData& videoConstraintsData)
{
#if ENABLE(MEDIA_STREAM)
RealtimeMediaSourceCenter::InvalidConstraintsHandler invalidHandler = [this, userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier](const String& invalidConstraint) {
if (!m_page.isValid())
return;
denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint, invalidConstraint);
};
RealtimeMediaSourceCenter::ValidConstraintsHandler validHandler = [this, userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier](const Vector<RefPtr<RealtimeMediaSource>>&& audioTracks, const Vector<RefPtr<RealtimeMediaSource>>&& videoTracks) {
if (!m_page.isValid())
return;
Vector<String> videoDeviceUIDs;
Vector<String> audioDeviceUIDs;
for (auto& audioTrack : audioTracks)
audioDeviceUIDs.append(audioTrack->persistentID());
for (auto& videoTrack : videoTracks)
videoDeviceUIDs.append(videoTrack->persistentID());
if (videoDeviceUIDs.isEmpty() && audioDeviceUIDs.isEmpty()) {
denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, "");
return;
}
auto userMediaOrigin = API::SecurityOrigin::create(SecurityOrigin::createFromDatabaseIdentifier(userMediaDocumentOriginIdentifier));
auto topLevelOrigin = API::SecurityOrigin::create(SecurityOrigin::createFromDatabaseIdentifier(topLevelDocumentOriginIdentifier));
auto request = createRequest(userMediaID, audioDeviceUIDs, videoDeviceUIDs);
if (!m_page.uiClient().decidePolicyForUserMediaPermissionRequest(m_page, *m_page.process().webFrame(frameID), *userMediaOrigin.get(), *topLevelOrigin.get(), request.get())) {
m_pendingUserMediaRequests.take(userMediaID);
request->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled);
}
};
auto audioConstraints = MediaConstraintsImpl::create(audioConstraintsData);
auto videoConstraints = MediaConstraintsImpl::create(videoConstraintsData);
syncWithWebCorePrefs();
RealtimeMediaSourceCenter::singleton().validateRequestConstraints(validHandler, invalidHandler, audioConstraints, videoConstraints);
#else
UNUSED_PARAM(userMediaID);
UNUSED_PARAM(frameID);
UNUSED_PARAM(userMediaDocumentOriginIdentifier);
UNUSED_PARAM(topLevelDocumentOriginIdentifier);
UNUSED_PARAM(audioConstraintsData);
UNUSED_PARAM(videoConstraintsData);
#endif
}
void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier)
{
#if ENABLE(MEDIA_STREAM)
auto request = UserMediaPermissionCheckProxy::create(*this, userMediaID);
m_pendingDeviceRequests.add(userMediaID, request.ptr());
auto userMediaOrigin = API::SecurityOrigin::create(SecurityOrigin::createFromDatabaseIdentifier(userMediaDocumentOriginIdentifier));
auto topLevelOrigin = API::SecurityOrigin::create(SecurityOrigin::createFromDatabaseIdentifier(topLevelDocumentOriginIdentifier));
if (!m_page.uiClient().checkUserMediaPermissionForOrigin(m_page, *m_page.process().webFrame(frameID), *userMediaOrigin.get(), *topLevelOrigin.get(), request.get())) {
m_pendingDeviceRequests.take(userMediaID);
m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, Vector<WebCore::CaptureDevice>(), emptyString(), false), m_page.pageID());
}
#else
UNUSED_PARAM(userMediaID);
UNUSED_PARAM(frameID);
UNUSED_PARAM(userMediaDocumentOriginIdentifier);
UNUSED_PARAM(topLevelDocumentOriginIdentifier);
#endif
}
void UserMediaPermissionRequestManagerProxy::didCompleteUserMediaPermissionCheck(uint64_t userMediaID, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)
{
if (!m_page.isValid())
return;
if (!m_pendingDeviceRequests.take(userMediaID))
return;
#if ENABLE(MEDIA_STREAM)
syncWithWebCorePrefs();
auto deviceInfo = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, deviceInfo, deviceIdentifierHashSalt, originHasPersistentAccess), m_page.pageID());
#else
UNUSED_PARAM(deviceIdentifierHashSalt);
UNUSED_PARAM(originHasPersistentAccess);
#endif
}
void UserMediaPermissionRequestManagerProxy::syncWithWebCorePrefs() const
{
#if ENABLE(MEDIA_STREAM)
// Enable/disable the mock capture devices for the UI process as per the WebCore preferences. Note that
// this is a noop if the preference hasn't changed since the last time this was called.
bool mockDevicesEnabled = m_page.preferences().mockCaptureDevicesEnabled();
WebCore::MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(mockDevicesEnabled);
#endif
}
} // namespace WebKit