1 | /* |
2 | * Copyright (C) 2017-2018 Igalia S.L. 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. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "VRDisplay.h" |
28 | |
29 | #include "CanvasRenderingContext.h" |
30 | #include "Chrome.h" |
31 | #include "DOMException.h" |
32 | #include "DOMWindow.h" |
33 | #include "EventNames.h" |
34 | #include "Page.h" |
35 | #include "ScriptedAnimationController.h" |
36 | #include "UserGestureIndicator.h" |
37 | #include "VRDisplayCapabilities.h" |
38 | #include "VRDisplayEvent.h" |
39 | #include "VREyeParameters.h" |
40 | #include "VRFrameData.h" |
41 | #include "VRLayerInit.h" |
42 | #include "VRPlatformDisplay.h" |
43 | #include "VRPose.h" |
44 | #include "VRStageParameters.h" |
45 | #include <wtf/IsoMallocInlines.h> |
46 | |
47 | namespace WebCore { |
48 | |
49 | WTF_MAKE_ISO_ALLOCATED_IMPL(VRDisplay); |
50 | |
51 | Ref<VRDisplay> VRDisplay::create(ScriptExecutionContext& context, WeakPtr<VRPlatformDisplay>&& platformDisplay) |
52 | { |
53 | return adoptRef(*new VRDisplay(context, WTFMove(platformDisplay))); |
54 | } |
55 | |
56 | VRDisplay::VRDisplay(ScriptExecutionContext& context, WeakPtr<VRPlatformDisplay>&& platformDisplay) |
57 | : ActiveDOMObject(&context) |
58 | , m_display(WTFMove(platformDisplay)) |
59 | { |
60 | auto displayInfo = m_display->getDisplayInfo(); |
61 | m_capabilities = VRDisplayCapabilities::create(displayInfo.capabilityFlags()); |
62 | m_leftEyeParameters = VREyeParameters::create(displayInfo.eyeTranslation(VRPlatformDisplayInfo::EyeLeft), displayInfo.eyeFieldOfView(VRPlatformDisplayInfo::EyeLeft), displayInfo.renderSize()); |
63 | m_rightEyeParameters = VREyeParameters::create(displayInfo.eyeTranslation(VRPlatformDisplayInfo::EyeRight), displayInfo.eyeFieldOfView(VRPlatformDisplayInfo::EyeRight), displayInfo.renderSize()); |
64 | m_displayId = displayInfo.displayIdentifier(); |
65 | m_displayName = displayInfo.displayName(); |
66 | |
67 | m_display->setClient(this); |
68 | suspendIfNeeded(); |
69 | } |
70 | |
71 | VRDisplay::~VRDisplay() |
72 | { |
73 | m_display->setClient(nullptr); |
74 | } |
75 | |
76 | bool VRDisplay::isConnected() const |
77 | { |
78 | if (!m_display) |
79 | return false; |
80 | |
81 | return m_display->getDisplayInfo().isConnected(); |
82 | } |
83 | |
84 | const VRDisplayCapabilities& VRDisplay::capabilities() const |
85 | { |
86 | return *m_capabilities; |
87 | } |
88 | |
89 | RefPtr<VRStageParameters> VRDisplay::stageParameters() const |
90 | { |
91 | auto displayInfo = m_display->getDisplayInfo(); |
92 | return VRStageParameters::create(displayInfo.sittingToStandingTransform(), displayInfo.playAreaBounds()); |
93 | } |
94 | |
95 | const VREyeParameters& VRDisplay::getEyeParameters(VREye eye) const |
96 | { |
97 | return eye == VREye::Left ? *m_leftEyeParameters : *m_rightEyeParameters; |
98 | } |
99 | |
100 | bool VRDisplay::getFrameData(VRFrameData& frameData) const |
101 | { |
102 | if (!m_capabilities->hasPosition() || !m_capabilities->hasOrientation()) |
103 | return false; |
104 | |
105 | // FIXME: ensure that this is only called inside WebVR's rAF. |
106 | frameData.update(m_display->getTrackingInfo(), getEyeParameters(VREye::Left), getEyeParameters(VREye::Right), m_depthNear, m_depthFar); |
107 | return true; |
108 | } |
109 | |
110 | Ref<VRPose> VRDisplay::getPose() const |
111 | { |
112 | return VRPose::create(m_display->getTrackingInfo()); |
113 | } |
114 | |
115 | void VRDisplay::resetPose() |
116 | { |
117 | } |
118 | |
119 | uint32_t VRDisplay::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callback) |
120 | { |
121 | if (!m_scriptedAnimationController) { |
122 | auto* document = downcast<Document>(scriptExecutionContext()); |
123 | m_scriptedAnimationController = ScriptedAnimationController::create(*document); |
124 | } |
125 | |
126 | return m_scriptedAnimationController->registerCallback(WTFMove(callback)); |
127 | } |
128 | |
129 | void VRDisplay::cancelAnimationFrame(uint32_t id) |
130 | { |
131 | if (!m_scriptedAnimationController) |
132 | return; |
133 | |
134 | m_scriptedAnimationController->cancelCallback(id); |
135 | } |
136 | |
137 | void VRDisplay::requestPresent(const Vector<VRLayerInit>& layers, Ref<DeferredPromise>&& promise) |
138 | { |
139 | auto rejectRequestAndStopPresenting = [this, &promise] (ExceptionCode exceptionCode, ASCIILiteral message) { |
140 | promise->reject(Exception { exceptionCode, message }); |
141 | if (m_presentingLayer) |
142 | stopPresenting(); |
143 | }; |
144 | |
145 | if (!m_capabilities->canPresent()) { |
146 | rejectRequestAndStopPresenting(NotSupportedError, "VRDisplay cannot present"_s ); |
147 | return; |
148 | } |
149 | |
150 | if (!layers.size() || layers.size() > m_capabilities->maxLayers()) { |
151 | rejectRequestAndStopPresenting(InvalidStateError, layers.size() ? "Too many layers"_s : "Not enough layers"_s ); |
152 | return; |
153 | } |
154 | |
155 | if (!m_presentingLayer && !UserGestureIndicator::processingUserGesture()) { |
156 | rejectRequestAndStopPresenting(InvalidAccessError, "Must request presentation from a user gesture handler."_s ); |
157 | return; |
158 | } |
159 | |
160 | RELEASE_ASSERT(layers.size() == 1); |
161 | auto layer = layers[0]; |
162 | |
163 | if (!layer.source) { |
164 | rejectRequestAndStopPresenting(InvalidStateError, "Layer does not contain any source"_s ); |
165 | return; |
166 | } |
167 | |
168 | auto* canvasContext = layer.source->getContext("webgl" ); |
169 | if (!canvasContext || !canvasContext->isWebGL()) { |
170 | rejectRequestAndStopPresenting(NotSupportedError, "WebVR requires VRLayerInit with WebGL context."_s ); |
171 | return; |
172 | } |
173 | |
174 | if ((layer.leftBounds.size() && layer.leftBounds.size() != 4) |
175 | || (layer.rightBounds.size() && layer.rightBounds.size() != 4)) { |
176 | rejectRequestAndStopPresenting(InvalidStateError, "Layer bounds must be either 0 or 4"_s ); |
177 | return; |
178 | } |
179 | |
180 | m_presentingLayer = layer; |
181 | promise->resolve(); |
182 | } |
183 | |
184 | void VRDisplay::stopPresenting() |
185 | { |
186 | m_presentingLayer = WTF::nullopt; |
187 | } |
188 | |
189 | void VRDisplay::exitPresent(Ref<DeferredPromise>&& promise) |
190 | { |
191 | if (!m_presentingLayer) { |
192 | promise->reject(Exception { InvalidStateError, "VRDisplay is not presenting"_s }); |
193 | return; |
194 | } |
195 | |
196 | stopPresenting(); |
197 | } |
198 | |
199 | Vector<VRLayerInit> VRDisplay::getLayers() const |
200 | { |
201 | Vector<VRLayerInit> layers; |
202 | if (m_presentingLayer) |
203 | layers.append(m_presentingLayer.value()); |
204 | return layers; |
205 | } |
206 | |
207 | void VRDisplay::submitFrame() |
208 | { |
209 | } |
210 | |
211 | void VRDisplay::platformDisplayConnected() |
212 | { |
213 | document()->domWindow()->dispatchEvent(VRDisplayEvent::create(eventNames().vrdisplayconnectEvent, makeRefPtr(this), WTF::nullopt)); |
214 | } |
215 | |
216 | void VRDisplay::platformDisplayDisconnected() |
217 | { |
218 | document()->domWindow()->dispatchEvent(VRDisplayEvent::create(eventNames().vrdisplaydisconnectEvent, makeRefPtr(this), WTF::nullopt)); |
219 | } |
220 | |
221 | void VRDisplay::platformDisplayMounted() |
222 | { |
223 | document()->domWindow()->dispatchEvent(VRDisplayEvent::create(eventNames().vrdisplayactivateEvent, makeRefPtr(this), VRDisplayEventReason::Mounted)); |
224 | } |
225 | |
226 | void VRDisplay::platformDisplayUnmounted() |
227 | { |
228 | document()->domWindow()->dispatchEvent(VRDisplayEvent::create(eventNames().vrdisplaydeactivateEvent, makeRefPtr(this), VRDisplayEventReason::Unmounted)); |
229 | } |
230 | |
231 | bool VRDisplay::hasPendingActivity() const |
232 | { |
233 | return false; |
234 | } |
235 | |
236 | const char* VRDisplay::activeDOMObjectName() const |
237 | { |
238 | return "VRDisplay" ; |
239 | } |
240 | |
241 | bool VRDisplay::canSuspendForDocumentSuspension() const |
242 | { |
243 | return false; |
244 | } |
245 | |
246 | void VRDisplay::stop() |
247 | { |
248 | } |
249 | |
250 | } // namespace WebCore |
251 | |