diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/UserMediaProcessManager.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/UserMediaProcessManager.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/UserMediaProcessManager.cpp b/Source/WebKit2/UIProcess/UserMediaProcessManager.cpp new file mode 100644 index 000000000..a0af85f6e --- /dev/null +++ b/Source/WebKit2/UIProcess/UserMediaProcessManager.cpp @@ -0,0 +1,202 @@ +/* + * 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 "UserMediaProcessManager.h" + +#if ENABLE(MEDIA_STREAM) + +#include "MediaDeviceSandboxExtensions.h" +#include "WebPageMessages.h" +#include "WebPageProxy.h" +#include "WebProcessProxy.h" +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> + +namespace WebKit { + +static const char* const audioExtensionPath = "com.apple.webkit.microphone"; +static const char* const videoExtensionPath = "com.apple.webkit.camera"; + +class ProcessState { +public: + ProcessState() { } + ProcessState(const ProcessState&) = delete; + + void addRequestManager(UserMediaPermissionRequestManagerProxy&); + void removeRequestManager(UserMediaPermissionRequestManagerProxy&); + Vector<UserMediaPermissionRequestManagerProxy*>& managers() { return m_managers; } + + enum SandboxExtensionsGranted { + None = 0, + Video = 1 << 0, + Audio = 1 << 1 + }; + + SandboxExtensionsGranted sandboxExtensionsGranted() { return m_pageSandboxExtensionsGranted; } + void setSandboxExtensionsGranted(unsigned granted) { m_pageSandboxExtensionsGranted = static_cast<SandboxExtensionsGranted>(granted); } + +private: + Vector<UserMediaPermissionRequestManagerProxy*> m_managers; + SandboxExtensionsGranted m_pageSandboxExtensionsGranted { SandboxExtensionsGranted::None }; +}; + +static HashMap<WebProcessProxy*, std::unique_ptr<ProcessState>>& stateMap() +{ + static NeverDestroyed<HashMap<WebProcessProxy*, std::unique_ptr<ProcessState>>> map; + return map; +} + +static ProcessState& processState(WebProcessProxy& process) +{ + auto& state = stateMap().add(&process, nullptr).iterator->value; + if (state) + return *state; + + state = std::make_unique<ProcessState>(); + return *state; +} + +void ProcessState::addRequestManager(UserMediaPermissionRequestManagerProxy& proxy) +{ + ASSERT(!m_managers.contains(&proxy)); + m_managers.append(&proxy); +} + +void ProcessState::removeRequestManager(UserMediaPermissionRequestManagerProxy& proxy) +{ + ASSERT(m_managers.contains(&proxy)); + m_managers.removeFirstMatching([&proxy](auto other) { + return other == &proxy; + }); +} + +UserMediaProcessManager& UserMediaProcessManager::singleton() +{ + static NeverDestroyed<UserMediaProcessManager> manager; + return manager; +} + +void UserMediaProcessManager::addUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy& proxy) +{ + processState(proxy.page().process()).addRequestManager(proxy); +} + +void UserMediaProcessManager::removeUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy& proxy) +{ + endedCaptureSession(proxy); + + auto& state = processState(proxy.page().process()); + state.removeRequestManager(proxy); + if (state.managers().isEmpty()) { + auto it = stateMap().find(&proxy.page().process()); + stateMap().remove(it); + } +} + +void UserMediaProcessManager::willCreateMediaStream(UserMediaPermissionRequestManagerProxy& proxy, bool withAudio, bool withVideo) +{ +#if ENABLE(SANDBOX_EXTENSIONS) + ASSERT(stateMap().contains(&proxy.page().process())); + + auto& state = processState(proxy.page().process()); + size_t extensionCount = 0; + unsigned requiredExtensions = ProcessState::SandboxExtensionsGranted::None; + + if (withAudio) { + requiredExtensions |= ProcessState::SandboxExtensionsGranted::Audio; + extensionCount++; + } + if (withVideo) { + requiredExtensions |= ProcessState::SandboxExtensionsGranted::Video; + extensionCount++; + } + + unsigned currentExtensions = state.sandboxExtensionsGranted(); + if (!(requiredExtensions & currentExtensions)) { + SandboxExtension::HandleArray handles; + handles.allocate(extensionCount); + + Vector<String> ids; + ids.reserveCapacity(extensionCount); + + if (withAudio && requiredExtensions & ProcessState::SandboxExtensionsGranted::Audio) { + if (SandboxExtension::createHandleForGenericExtension(audioExtensionPath, handles[--extensionCount])) { + ids.append(ASCIILiteral(audioExtensionPath)); + currentExtensions |= ProcessState::SandboxExtensionsGranted::Audio; + } + } + + if (withVideo && requiredExtensions & ProcessState::SandboxExtensionsGranted::Video) { + if (SandboxExtension::createHandleForGenericExtension(videoExtensionPath, handles[--extensionCount])) { + ids.append(ASCIILiteral(videoExtensionPath)); + currentExtensions |= ProcessState::SandboxExtensionsGranted::Video; + } + } + + state.setSandboxExtensionsGranted(currentExtensions); + proxy.page().process().send(Messages::WebPage::GrantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions(ids, WTFMove(handles))), proxy.page().pageID()); + } +#endif +} + +void UserMediaProcessManager::startedCaptureSession(UserMediaPermissionRequestManagerProxy& proxy) +{ + ASSERT(stateMap().contains(&proxy.page().process())); +} + +void UserMediaProcessManager::endedCaptureSession(UserMediaPermissionRequestManagerProxy& proxy) +{ +#if ENABLE(SANDBOX_EXTENSIONS) + ASSERT(stateMap().contains(&proxy.page().process())); + + auto& state = processState(proxy.page().process()); + bool hasAudioCapture = false; + bool hasVideoCapture = false; + for (auto& manager : state.managers()) { + if (manager->page().hasActiveAudioStream()) + hasAudioCapture = true; + if (manager->page().hasActiveVideoStream()) + hasVideoCapture = true; + } + + if (hasAudioCapture && hasVideoCapture) + return; + + Vector<String> params; + unsigned currentExtensions = state.sandboxExtensionsGranted(); + if (!hasAudioCapture && currentExtensions & ProcessState::SandboxExtensionsGranted::Audio) { + params.append(ASCIILiteral(audioExtensionPath)); + currentExtensions &= ~ProcessState::SandboxExtensionsGranted::Audio; + } + if (!hasVideoCapture && currentExtensions & ProcessState::SandboxExtensionsGranted::Video) { + params.append(ASCIILiteral(videoExtensionPath)); + currentExtensions &= ~ProcessState::SandboxExtensionsGranted::Video; + } + + if (params.isEmpty()) + return; + + state.setSandboxExtensionsGranted(currentExtensions); + proxy.page().process().send(Messages::WebPage::RevokeUserMediaDeviceSandboxExtensions(params), proxy.page().pageID()); +#endif +} + +} // namespace WebKit + +#endif |