Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / Modules / mediastream / UserMediaRequest.cpp
1 /*
2  * Copyright (C) 2011 Ericsson AB. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
5  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer
15  *    in the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of Ericsson nor the names of its contributors
18  *    may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "config.h"
35 #include "UserMediaRequest.h"
36
37 #if ENABLE(MEDIA_STREAM)
38
39 #include "DeprecatedGlobalSettings.h"
40 #include "Document.h"
41 #include "DocumentLoader.h"
42 #include "JSMediaStream.h"
43 #include "JSOverconstrainedError.h"
44 #include "Logging.h"
45 #include "MainFrame.h"
46 #include "MediaConstraints.h"
47 #include "RealtimeMediaSourceCenter.h"
48 #include "SchemeRegistry.h"
49 #include "Settings.h"
50 #include "UserMediaController.h"
51
52 namespace WebCore {
53
54 ExceptionOr<void> UserMediaRequest::start(Document& document, MediaConstraints&& audioConstraints, MediaConstraints&& videoConstraints, DOMPromiseDeferred<IDLInterface<MediaStream>>&& promise)
55 {
56     auto* userMedia = UserMediaController::from(document.page());
57     if (!userMedia)
58         return Exception { NotSupportedError }; // FIXME: Why is it better to return an exception here instead of rejecting the promise as we do just below?
59
60     if (!audioConstraints.isValid && !videoConstraints.isValid) {
61         promise.reject(TypeError);
62         return { };
63     }
64
65     adoptRef(*new UserMediaRequest(document, *userMedia, WTFMove(audioConstraints), WTFMove(videoConstraints), WTFMove(promise)))->start();
66     return { };
67 }
68
69 UserMediaRequest::UserMediaRequest(Document& document, UserMediaController& controller, MediaConstraints&& audioConstraints, MediaConstraints&& videoConstraints, DOMPromiseDeferred<IDLInterface<MediaStream>>&& promise)
70     : ContextDestructionObserver(&document)
71     , m_audioConstraints(WTFMove(audioConstraints))
72     , m_videoConstraints(WTFMove(videoConstraints))
73     , m_controller(&controller)
74     , m_promise(WTFMove(promise))
75 {
76 }
77
78 UserMediaRequest::~UserMediaRequest() = default;
79
80 SecurityOrigin* UserMediaRequest::userMediaDocumentOrigin() const
81 {
82     if (!m_scriptExecutionContext)
83         return nullptr;
84     return m_scriptExecutionContext->securityOrigin();
85 }
86
87 SecurityOrigin* UserMediaRequest::topLevelDocumentOrigin() const
88 {
89     if (!m_scriptExecutionContext)
90         return nullptr;
91     return &m_scriptExecutionContext->topOrigin();
92 }
93
94 static bool isSecure(DocumentLoader& documentLoader)
95 {
96     auto& response = documentLoader.response();
97     return SchemeRegistry::shouldTreatURLSchemeAsSecure(response.url().protocol().toStringWithoutCopying())
98         && response.certificateInfo()
99         && !response.certificateInfo()->containsNonRootSHA1SignedCertificate();
100 }
101
102 static bool canCallGetUserMedia(Document& document, String& errorMessage)
103 {
104     bool requiresSecureConnection = DeprecatedGlobalSettings::mediaCaptureRequiresSecureConnection();
105     auto& documentLoader = *document.loader();
106     if (requiresSecureConnection && !isSecure(documentLoader) && !SecurityOrigin::isLocalHostOrLoopbackIPAddress(documentLoader.response().url())) {
107         errorMessage = "Trying to call getUserMedia from an insecure document.";
108         return false;
109     }
110
111     auto& topDocument = document.topDocument();
112     if (&document != &topDocument) {
113         auto& topOrigin = topDocument.topOrigin();
114
115         if (!document.securityOrigin().isSameSchemeHostPort(topOrigin)) {
116             errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
117             return false;
118         }
119
120         for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
121             if (requiresSecureConnection && !isSecure(*ancestorDocument->loader())) {
122                 errorMessage = "Trying to call getUserMedia from a document with an insecure parent frame.";
123                 return false;
124             }
125
126             if (!ancestorDocument->securityOrigin().isSameSchemeHostPort(topOrigin)) {
127                 errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
128                 return false;
129             }
130         }
131     }
132     
133     return true;
134 }
135
136 void UserMediaRequest::start()
137 {
138     if (!m_scriptExecutionContext || !m_controller) {
139         deny(MediaAccessDenialReason::OtherFailure, emptyString());
140         return;
141     }
142
143     Document& document = downcast<Document>(*m_scriptExecutionContext);
144
145     // 10.2 - 6.3 Optionally, e.g., based on a previously-established user preference, for security reasons,
146     // or due to platform limitations, jump to the step labeled Permission Failure below.
147     String errorMessage;
148     if (!canCallGetUserMedia(document, errorMessage)) {
149         deny(MediaAccessDenialReason::PermissionDenied, emptyString());
150         document.domWindow()->printErrorMessage(errorMessage);
151         return;
152     }
153
154     m_controller->requestUserMediaAccess(*this);
155 }
156
157 void UserMediaRequest::allow(String&& audioDeviceUID, String&& videoDeviceUID, String&& deviceIdentifierHashSalt)
158 {
159     RELEASE_LOG(MediaStream, "UserMediaRequest::allow %s %s", audioDeviceUID.utf8().data(), videoDeviceUID.utf8().data());
160     m_allowedAudioDeviceUID = WTFMove(audioDeviceUID);
161     m_allowedVideoDeviceUID = WTFMove(videoDeviceUID);
162
163     RefPtr<UserMediaRequest> protectedThis = this;
164     RealtimeMediaSourceCenter::NewMediaStreamHandler callback = [this, protectedThis = WTFMove(protectedThis)](RefPtr<MediaStreamPrivate>&& privateStream) mutable {
165         if (!m_scriptExecutionContext)
166             return;
167
168         if (!privateStream) {
169             deny(MediaAccessDenialReason::HardwareError, emptyString());
170             return;
171         }
172         privateStream->monitorOrientation(downcast<Document>(m_scriptExecutionContext)->orientationNotifier());
173
174         auto stream = MediaStream::create(*m_scriptExecutionContext, privateStream.releaseNonNull());
175         if (stream->getTracks().isEmpty()) {
176             deny(MediaAccessDenialReason::HardwareError, emptyString());
177             return;
178         }
179
180         stream->startProducingData();
181         
182         m_promise.resolve(stream);
183     };
184
185     m_audioConstraints.deviceIDHashSalt = deviceIdentifierHashSalt;
186     m_videoConstraints.deviceIDHashSalt = WTFMove(deviceIdentifierHashSalt);
187
188     RealtimeMediaSourceCenter::singleton().createMediaStream(WTFMove(callback), m_allowedAudioDeviceUID, m_allowedVideoDeviceUID, &m_audioConstraints, &m_videoConstraints);
189
190     if (!m_scriptExecutionContext)
191         return;
192
193 #if ENABLE(WEB_RTC)
194     auto* page = downcast<Document>(*m_scriptExecutionContext).page();
195     if (page)
196         page->rtcController().disableICECandidateFiltering();
197 #endif
198 }
199
200 void UserMediaRequest::deny(MediaAccessDenialReason reason, const String& invalidConstraint)
201 {
202     if (!m_scriptExecutionContext)
203         return;
204
205     switch (reason) {
206     case MediaAccessDenialReason::NoConstraints:
207         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - no constraints");
208         m_promise.reject(TypeError);
209         break;
210     case MediaAccessDenialReason::UserMediaDisabled:
211         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - user media disabled");
212         m_promise.reject(SecurityError);
213         break;
214     case MediaAccessDenialReason::NoCaptureDevices:
215         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - no capture devices");
216         m_promise.reject(NotFoundError);
217         break;
218     case MediaAccessDenialReason::InvalidConstraint:
219         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - invalid constraint - %s", invalidConstraint.utf8().data());
220         m_promise.rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(invalidConstraint, ASCIILiteral("Invalid constraint")).get());
221         break;
222     case MediaAccessDenialReason::HardwareError:
223         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - hardware error");
224         m_promise.reject(NotReadableError);
225         break;
226     case MediaAccessDenialReason::OtherFailure:
227         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - other failure");
228         m_promise.reject(AbortError);
229         break;
230     case MediaAccessDenialReason::PermissionDenied:
231         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - permission denied");
232         m_promise.reject(NotAllowedError);
233         break;
234     }
235 }
236
237 void UserMediaRequest::contextDestroyed()
238 {
239     ContextDestructionObserver::contextDestroyed();
240     Ref<UserMediaRequest> protectedThis(*this);
241     if (m_controller) {
242         m_controller->cancelUserMediaAccessRequest(*this);
243         m_controller = nullptr;
244     }
245 }
246
247 Document* UserMediaRequest::document() const
248 {
249     return downcast<Document>(m_scriptExecutionContext);
250 }
251
252 } // namespace WebCore
253
254 #endif // ENABLE(MEDIA_STREAM)