[MediaStream] defer resolution of getUserMedia promise made in a background tab
[WebKit-https.git] / Source / WebKit2 / WebProcess / MediaStream / UserMediaPermissionRequestManager.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  * Copyright (C) 2016 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "UserMediaPermissionRequestManager.h"
22
23 #if ENABLE(MEDIA_STREAM)
24
25 #include "WebFrame.h"
26 #include "WebPage.h"
27 #include "WebPageProxyMessages.h"
28 #include <WebCore/CaptureDevice.h>
29 #include <WebCore/Document.h>
30 #include <WebCore/Frame.h>
31 #include <WebCore/FrameLoader.h>
32 #include <WebCore/SecurityOrigin.h>
33 #include <WebCore/SecurityOriginData.h>
34
35 using namespace WebCore;
36
37 namespace WebKit {
38
39 using namespace WebCore;
40
41 static uint64_t generateRequestID()
42 {
43     static uint64_t uniqueRequestID = 1;
44     return uniqueRequestID++;
45 }
46
47 UserMediaPermissionRequestManager::UserMediaPermissionRequestManager(WebPage& page)
48     : m_page(page)
49 {
50 }
51
52 UserMediaPermissionRequestManager::~UserMediaPermissionRequestManager()
53 {
54     for (auto& sandboxExtension : m_userMediaDeviceSandboxExtensions)
55         sandboxExtension->revoke();
56 }
57
58 void UserMediaPermissionRequestManager::startUserMediaRequest(UserMediaRequest& request)
59 {
60     Document* document = request.document();
61     Frame* frame = document ? document->frame() : nullptr;
62
63     if (!frame || !document->page()) {
64         request.deny(UserMediaRequest::OtherFailure, emptyString());
65         return;
66     }
67
68     if (document->page()->canStartMedia()) {
69         sendUserMediaRequest(request);
70         return;
71     }
72
73     auto& pendingRequests = m_blockedRequests.add(document, Vector<RefPtr<UserMediaRequest>>()).iterator->value;
74     if (pendingRequests.isEmpty())
75         document->addMediaCanStartListener(this);
76     pendingRequests.append(&request);
77 }
78
79 void UserMediaPermissionRequestManager::sendUserMediaRequest(UserMediaRequest& request)
80 {
81     Document* document = request.document();
82     Frame* frame = document ? document->frame() : nullptr;
83
84     if (!frame) {
85         request.deny(UserMediaRequest::OtherFailure, emptyString());
86         return;
87     }
88
89     uint64_t requestID = generateRequestID();
90     m_idToUserMediaRequestMap.add(requestID, &request);
91     m_userMediaRequestToIDMap.add(&request, requestID);
92
93     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
94     ASSERT(webFrame);
95
96     SecurityOrigin* topLevelDocumentOrigin = request.topLevelDocumentOrigin();
97     String topLevelDocumentOriginString = topLevelDocumentOrigin ? SecurityOriginData::fromSecurityOrigin(*topLevelDocumentOrigin).databaseIdentifier() : emptyString();
98     m_page.send(Messages::WebPageProxy::RequestUserMediaPermissionForFrame(requestID, webFrame->frameID(), SecurityOriginData::fromSecurityOrigin(*request.userMediaDocumentOrigin()).databaseIdentifier(), topLevelDocumentOriginString, request.audioConstraints().data(), request.videoConstraints().data()));
99 }
100
101 void UserMediaPermissionRequestManager::cancelUserMediaRequest(UserMediaRequest& request)
102 {
103     uint64_t requestID = m_userMediaRequestToIDMap.take(&request);
104     if (!requestID)
105         return;
106     m_idToUserMediaRequestMap.remove(requestID);
107     removeMediaRequestFromMaps(request);
108 }
109
110 void UserMediaPermissionRequestManager::mediaCanStart(Document& document)
111 {
112     auto pendingRequests = m_blockedRequests.take(&document);
113     while (!pendingRequests.isEmpty()) {
114         if (!document.page()->canStartMedia()) {
115             m_blockedRequests.add(&document, pendingRequests);
116             document.addMediaCanStartListener(this);
117             break;
118         }
119
120         sendUserMediaRequest(*pendingRequests.takeLast());
121     }
122 }
123
124 void UserMediaPermissionRequestManager::removeMediaRequestFromMaps(UserMediaRequest& request)
125 {
126     auto pendingRequests = m_blockedRequests.take(request.document());
127     for (auto& pendingRequest : pendingRequests) {
128         if (&request != pendingRequest.get())
129             continue;
130
131         if (pendingRequests.isEmpty())
132             request.document()->removeMediaCanStartListener(this);
133         else
134             m_blockedRequests.add(request.document(), pendingRequests);
135         break;
136     }
137
138     m_userMediaRequestToIDMap.remove(&request);
139 }
140
141 void UserMediaPermissionRequestManager::userMediaAccessWasGranted(uint64_t requestID, const String& audioDeviceUID, const String& videoDeviceUID)
142 {
143     auto request = m_idToUserMediaRequestMap.take(requestID);
144     if (!request)
145         return;
146     removeMediaRequestFromMaps(*request);
147
148     request->allow(audioDeviceUID, videoDeviceUID);
149 }
150
151 void UserMediaPermissionRequestManager::userMediaAccessWasDenied(uint64_t requestID, WebCore::UserMediaRequest::MediaAccessDenialReason reason, const String& invalidConstraint)
152 {
153     auto request = m_idToUserMediaRequestMap.take(requestID);
154     if (!request)
155         return;
156     removeMediaRequestFromMaps(*request);
157
158     request->deny(reason, invalidConstraint);
159 }
160
161 void UserMediaPermissionRequestManager::enumerateMediaDevices(MediaDevicesEnumerationRequest& request)
162 {
163     auto* document = downcast<Document>(request.scriptExecutionContext());
164     auto* frame = document ? document->frame() : nullptr;
165
166     if (!frame) {
167         request.setDeviceInfo(Vector<CaptureDevice>(), emptyString(), false);
168         return;
169     }
170
171     uint64_t requestID = generateRequestID();
172     m_idToMediaDevicesEnumerationRequestMap.add(requestID, &request);
173     m_mediaDevicesEnumerationRequestToIDMap.add(&request, requestID);
174
175     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
176     ASSERT(webFrame);
177
178     SecurityOrigin* topLevelDocumentOrigin = request.topLevelDocumentOrigin();
179     String topLevelDocumentOriginString = topLevelDocumentOrigin ? SecurityOriginData::fromSecurityOrigin(*topLevelDocumentOrigin).databaseIdentifier() : emptyString();
180     m_page.send(Messages::WebPageProxy::EnumerateMediaDevicesForFrame(requestID, webFrame->frameID(), SecurityOriginData::fromSecurityOrigin(*request.userMediaDocumentOrigin()).databaseIdentifier(), topLevelDocumentOriginString));
181 }
182
183 void UserMediaPermissionRequestManager::cancelMediaDevicesEnumeration(WebCore::MediaDevicesEnumerationRequest& request)
184 {
185     uint64_t requestID = m_mediaDevicesEnumerationRequestToIDMap.take(&request);
186     if (!requestID)
187         return;
188     m_idToMediaDevicesEnumerationRequestMap.remove(requestID);
189 }
190
191 void UserMediaPermissionRequestManager::didCompleteMediaDeviceEnumeration(uint64_t requestID, const Vector<CaptureDevice>& deviceList, const String& mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
192 {
193     RefPtr<MediaDevicesEnumerationRequest> request = m_idToMediaDevicesEnumerationRequestMap.take(requestID);
194     if (!request)
195         return;
196     m_mediaDevicesEnumerationRequestToIDMap.remove(request);
197     
198     request->setDeviceInfo(deviceList, mediaDeviceIdentifierHashSalt, hasPersistentAccess);
199 }
200
201 void UserMediaPermissionRequestManager::grantUserMediaDevicesSandboxExtension(const SandboxExtension::HandleArray& sandboxExtensionHandles)
202 {
203     ASSERT(m_userMediaDeviceSandboxExtensions.size() <= 2);
204
205     for (size_t i = 0; i < sandboxExtensionHandles.size(); i++) {
206         if (auto extension = SandboxExtension::create(sandboxExtensionHandles[i])) {
207             extension->consume();
208             m_userMediaDeviceSandboxExtensions.append(extension.release());
209         }
210     }
211 }
212
213 } // namespace WebKit
214
215 #endif // ENABLE(MEDIA_STREAM)