1/*
2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
27#include "config.h"
28#include "MediaDevicesRequest.h"
29
30#if ENABLE(MEDIA_STREAM)
31
32#include "CaptureDevice.h"
33#include "Document.h"
34#include "Frame.h"
35#include "JSMediaDeviceInfo.h"
36#include "MediaDevicesEnumerationRequest.h"
37#include "RealtimeMediaSourceCenter.h"
38#include "SecurityOrigin.h"
39#include "UserMediaController.h"
40#include <wtf/MainThread.h>
41#include <wtf/SHA1.h>
42
43namespace WebCore {
44
45inline MediaDevicesRequest::MediaDevicesRequest(Document& document, MediaDevices::EnumerateDevicesPromise&& promise)
46 : ContextDestructionObserver(&document)
47 , m_promise(WTFMove(promise))
48{
49}
50
51Ref<MediaDevicesRequest> MediaDevicesRequest::create(Document& document, MediaDevices::EnumerateDevicesPromise&& promise)
52{
53 return adoptRef(*new MediaDevicesRequest(document, WTFMove(promise)));
54}
55
56MediaDevicesRequest::~MediaDevicesRequest()
57{
58 // This should only get destroyed after the enumeration request has completed or has been canceled.
59 ASSERT(!m_enumerationRequest || m_enumerationRequest->wasCanceled());
60}
61
62SecurityOrigin* MediaDevicesRequest::securityOrigin() const
63{
64 if (scriptExecutionContext())
65 return scriptExecutionContext()->securityOrigin();
66
67 return nullptr;
68}
69
70void MediaDevicesRequest::contextDestroyed()
71{
72 // The call to m_enumerationRequest->cancel() might delete this.
73 auto protectedThis = makeRef(*this);
74
75 if (m_enumerationRequest) {
76 m_enumerationRequest->cancel();
77 m_enumerationRequest = nullptr;
78 }
79 ContextDestructionObserver::contextDestroyed();
80}
81
82void MediaDevicesRequest::start()
83{
84 auto& document = downcast<Document>(*scriptExecutionContext());
85 auto* controller = UserMediaController::from(document.page());
86 if (!controller) {
87 callOnMainThread([protectedThis = makeRef(*this)]() {
88 protectedThis->m_promise.resolve({ });
89 });
90
91 return;
92 }
93
94 auto microphoneAccess = controller->canCallGetUserMedia(document, { UserMediaController::CaptureType::Microphone });
95 auto cameraAccess = controller->canCallGetUserMedia(document, { UserMediaController::CaptureType::Camera });
96 bool canAccessMicrophone = microphoneAccess == UserMediaController::GetUserMediaAccess::CanCall;
97 bool canAccessCamera = cameraAccess == UserMediaController::GetUserMediaAccess::CanCall;
98 if (!canAccessMicrophone && !canAccessCamera) {
99 controller->logGetUserMediaDenial(document, !canAccessMicrophone ? microphoneAccess : cameraAccess, UserMediaController::BlockedCaller::EnumerateDevices);
100 callOnMainThread([protectedThis = makeRef(*this)]() {
101 protectedThis->m_promise.resolve({ });
102 });
103
104 return;
105 }
106
107 // This lambda keeps |this| alive until the request completes or is canceled.
108 auto completion = [this, protectedThis = makeRef(*this), canAccessMicrophone, canAccessCamera] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool) mutable {
109
110 m_enumerationRequest = nullptr;
111
112 if (!scriptExecutionContext())
113 return;
114
115 auto& document = downcast<Document>(*scriptExecutionContext());
116 document.setDeviceIDHashSalt(deviceIdentifierHashSalt);
117
118 Vector<Ref<MediaDeviceInfo>> devices;
119 for (auto& deviceInfo : captureDevices) {
120 if (!canAccessMicrophone && deviceInfo.type() == CaptureDevice::DeviceType::Microphone)
121 continue;
122 if (!canAccessCamera && deviceInfo.type() == CaptureDevice::DeviceType::Camera)
123 continue;
124
125 auto deviceType = deviceInfo.type() == CaptureDevice::DeviceType::Microphone ? MediaDeviceInfo::Kind::Audioinput : MediaDeviceInfo::Kind::Videoinput;
126 devices.append(MediaDeviceInfo::create(scriptExecutionContext(), deviceInfo.label(), deviceInfo.persistentId(), deviceInfo.groupId(), deviceType));
127 }
128
129 callOnMainThread([protectedThis = makeRef(*this), devices = WTFMove(devices)]() mutable {
130 protectedThis->m_promise.resolve(devices);
131 });
132 };
133
134 m_enumerationRequest = MediaDevicesEnumerationRequest::create(document, WTFMove(completion));
135 m_enumerationRequest->start();
136}
137
138} // namespace WebCore
139
140#endif // ENABLE(MEDIA_STREAM)
141