Rollout r231818, as it introduced regression on tickets.com.
[WebKit-https.git] / Source / WebKit / UIProcess / UserMediaProcessManager.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "UserMediaProcessManager.h"
21
22 #if ENABLE(MEDIA_STREAM)
23
24 #include "MediaDeviceSandboxExtensions.h"
25 #include "WebPageMessages.h"
26 #include "WebPageProxy.h"
27 #include "WebProcessProxy.h"
28 #include <wtf/HashMap.h>
29 #include <wtf/NeverDestroyed.h>
30
31 namespace WebKit {
32
33 #if ENABLE(SANDBOX_EXTENSIONS)
34 static const char* const audioExtensionPath = "com.apple.webkit.microphone";
35 static const char* const videoExtensionPath = "com.apple.webkit.camera";
36 #endif
37
38 class ProcessState {
39 public:
40     ProcessState() { }
41     ProcessState(const ProcessState&) = delete;
42
43     void addRequestManager(UserMediaPermissionRequestManagerProxy&);
44     void removeRequestManager(UserMediaPermissionRequestManagerProxy&);
45     Vector<UserMediaPermissionRequestManagerProxy*>& managers() { return m_managers; }
46
47     enum SandboxExtensionsGranted {
48         None = 0,
49         Video = 1 << 0,
50         Audio = 1 << 1
51     };
52
53     SandboxExtensionsGranted sandboxExtensionsGranted() { return m_pageSandboxExtensionsGranted; }
54     void setSandboxExtensionsGranted(unsigned granted) { m_pageSandboxExtensionsGranted = static_cast<SandboxExtensionsGranted>(granted); }
55
56 private:
57     Vector<UserMediaPermissionRequestManagerProxy*> m_managers;
58     SandboxExtensionsGranted m_pageSandboxExtensionsGranted { SandboxExtensionsGranted::None };
59 };
60
61 static HashMap<WebProcessProxy*, std::unique_ptr<ProcessState>>& stateMap()
62 {
63     static NeverDestroyed<HashMap<WebProcessProxy*, std::unique_ptr<ProcessState>>> map;
64     return map;
65 }
66
67 static ProcessState& processState(WebProcessProxy& process)
68 {
69     auto& state = stateMap().add(&process, nullptr).iterator->value;
70     if (state)
71         return *state;
72
73     state = std::make_unique<ProcessState>();
74     return *state;
75 }
76
77 void ProcessState::addRequestManager(UserMediaPermissionRequestManagerProxy& proxy)
78 {
79     ASSERT(!m_managers.contains(&proxy));
80     m_managers.append(&proxy);
81 }
82
83 void ProcessState::removeRequestManager(UserMediaPermissionRequestManagerProxy& proxy)
84 {
85     ASSERT(m_managers.contains(&proxy));
86     m_managers.removeFirstMatching([&proxy](auto other) {
87         return other == &proxy;
88     });
89 }
90
91 UserMediaProcessManager& UserMediaProcessManager::singleton()
92 {
93     static NeverDestroyed<UserMediaProcessManager> manager;
94     return manager;
95 }
96
97 void UserMediaProcessManager::addUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy& proxy)
98 {
99     processState(proxy.page().process()).addRequestManager(proxy);
100 }
101
102 void UserMediaProcessManager::removeUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy& proxy)
103 {
104     endedCaptureSession(proxy);
105
106     auto& state = processState(proxy.page().process());
107     state.removeRequestManager(proxy);
108     if (state.managers().isEmpty()) {
109         auto it = stateMap().find(&proxy.page().process());
110         stateMap().remove(it);
111     }
112 }
113
114 void UserMediaProcessManager::muteCaptureMediaStreamsExceptIn(WebPageProxy& pageStartingCapture)
115 {
116 #if PLATFORM(COCOA)
117     for (auto& state : stateMap()) {
118         for (auto& manager : state.value->managers()) {
119             if (&manager->page() == &pageStartingCapture)
120                 continue;
121             manager->page().setMediaStreamCaptureMuted(true);
122         }
123     }
124 #else
125     UNUSED_PARAM(pageStartingCapture);
126 #endif
127 }
128
129 bool UserMediaProcessManager::willCreateMediaStream(UserMediaPermissionRequestManagerProxy& proxy, bool withAudio, bool withVideo)
130 {
131     if (m_denyNextRequest) {
132         m_denyNextRequest = false;
133         return false;
134     }
135     
136     if (proxy.page().preferences().mockCaptureDevicesEnabled())
137         return true;
138     
139 #if ENABLE(SANDBOX_EXTENSIONS) && USE(APPLE_INTERNAL_SDK)
140     auto& processStartingCapture = proxy.page().process();
141
142     ASSERT(stateMap().contains(&processStartingCapture));
143
144     auto& state = processState(processStartingCapture);
145     size_t extensionCount = 0;
146     unsigned requiredExtensions = ProcessState::SandboxExtensionsGranted::None;
147
148     if (withAudio) {
149         requiredExtensions |= ProcessState::SandboxExtensionsGranted::Audio;
150         extensionCount++;
151     }
152     if (withVideo) {
153         requiredExtensions |= ProcessState::SandboxExtensionsGranted::Video;
154         extensionCount++;
155     }
156
157     unsigned currentExtensions = state.sandboxExtensionsGranted();
158
159     if (!(requiredExtensions & currentExtensions)) {
160         SandboxExtension::HandleArray handles;
161         handles.allocate(extensionCount);
162
163         Vector<String> ids;
164         ids.reserveCapacity(extensionCount);
165
166         if (withAudio && requiredExtensions & ProcessState::SandboxExtensionsGranted::Audio) {
167             if (SandboxExtension::createHandleForGenericExtension(audioExtensionPath, handles[--extensionCount])) {
168                 ids.append(ASCIILiteral(audioExtensionPath));
169                 currentExtensions |= ProcessState::SandboxExtensionsGranted::Audio;
170             }
171         }
172
173         if (withVideo && requiredExtensions & ProcessState::SandboxExtensionsGranted::Video) {
174             if (SandboxExtension::createHandleForGenericExtension(videoExtensionPath, handles[--extensionCount])) {
175                 ids.append(ASCIILiteral(videoExtensionPath));
176                 currentExtensions |= ProcessState::SandboxExtensionsGranted::Video;
177             }
178         }
179
180         if (ids.size() != handles.size()) {
181             WTFLogAlways("Could not create a required sandbox extension, capture will fail!");
182             return false;
183         }
184
185         state.setSandboxExtensionsGranted(currentExtensions);
186         processStartingCapture.send(Messages::WebPage::GrantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions(ids, WTFMove(handles))), proxy.page().pageID());
187     }
188 #else
189     UNUSED_PARAM(proxy);
190     UNUSED_PARAM(withAudio);
191     UNUSED_PARAM(withVideo);
192 #endif
193
194     proxy.page().activateMediaStreamCaptureInPage();
195
196     return true;
197 }
198
199 void UserMediaProcessManager::startedCaptureSession(UserMediaPermissionRequestManagerProxy& proxy)
200 {
201     ASSERT(stateMap().contains(&proxy.page().process()));
202 }
203
204 void UserMediaProcessManager::endedCaptureSession(UserMediaPermissionRequestManagerProxy& proxy)
205 {
206 #if ENABLE(SANDBOX_EXTENSIONS)
207     ASSERT(stateMap().contains(&proxy.page().process()));
208
209     auto& state = processState(proxy.page().process());
210     bool hasAudioCapture = false;
211     bool hasVideoCapture = false;
212     for (auto& manager : state.managers()) {
213         if (manager->page().hasActiveAudioStream())
214             hasAudioCapture = true;
215         if (manager->page().hasActiveVideoStream())
216             hasVideoCapture = true;
217     }
218
219     if (hasAudioCapture && hasVideoCapture)
220         return;
221
222     Vector<String> params;
223     unsigned currentExtensions = state.sandboxExtensionsGranted();
224     if (!hasAudioCapture && currentExtensions & ProcessState::SandboxExtensionsGranted::Audio) {
225         params.append(ASCIILiteral(audioExtensionPath));
226         currentExtensions &= ~ProcessState::SandboxExtensionsGranted::Audio;
227     }
228     if (!hasVideoCapture && currentExtensions & ProcessState::SandboxExtensionsGranted::Video) {
229         params.append(ASCIILiteral(videoExtensionPath));
230         currentExtensions &= ~ProcessState::SandboxExtensionsGranted::Video;
231     }
232
233     if (params.isEmpty())
234         return;
235
236     state.setSandboxExtensionsGranted(currentExtensions);
237     proxy.page().process().send(Messages::WebPage::RevokeUserMediaDeviceSandboxExtensions(params), proxy.page().pageID());
238 #endif
239 }
240
241 void UserMediaProcessManager::setCaptureEnabled(bool enabled)
242 {
243     if (enabled == m_captureEnabled)
244         return;
245
246     m_captureEnabled = enabled;
247
248     if (enabled)
249         return;
250
251     for (auto& state : stateMap()) {
252         for (auto& manager : state.value->managers())
253             manager->stopCapture();
254     }
255 }
256
257 } // namespace WebKit
258
259 #endif