|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "modules/xr/XRDevice.h" | 
|  |  | 
|  | #include "bindings/core/v8/ScriptPromiseResolver.h" | 
|  | #include "core/dom/DOMException.h" | 
|  | #include "core/frame/Frame.h" | 
|  | #include "core/frame/LocalFrame.h" | 
|  | #include "modules/EventTargetModules.h" | 
|  | #include "modules/xr/XR.h" | 
|  | #include "modules/xr/XRFrameProvider.h" | 
|  | #include "modules/xr/XRSession.h" | 
|  |  | 
|  | namespace blink { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kActiveExclusiveSession[] = | 
|  | "XRDevice already has an active, exclusive session"; | 
|  |  | 
|  | const char kExclusiveNotSupported[] = | 
|  | "XRDevice does not support the creation of exclusive sessions."; | 
|  |  | 
|  | const char kNoOutputContext[] = | 
|  | "Non-exclusive sessions must be created with an outputContext."; | 
|  |  | 
|  | const char kRequestNotInUserGesture[] = | 
|  | "Exclusive sessions can only be requested during a user gesture."; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | XRDevice::XRDevice( | 
|  | XR* xr, | 
|  | device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider, | 
|  | device::mojom::blink::VRDisplayHostPtr display, | 
|  | device::mojom::blink::VRDisplayClientRequest client_request, | 
|  | device::mojom::blink::VRDisplayInfoPtr display_info) | 
|  | : xr_(xr), | 
|  | magic_window_provider_(std::move(magic_window_provider)), | 
|  | display_(std::move(display)), | 
|  | display_client_binding_(this, std::move(client_request)) { | 
|  | SetXRDisplayInfo(std::move(display_info)); | 
|  | } | 
|  |  | 
|  | ExecutionContext* XRDevice::GetExecutionContext() const { | 
|  | return xr_->GetExecutionContext(); | 
|  | } | 
|  |  | 
|  | const AtomicString& XRDevice::InterfaceName() const { | 
|  | return EventTargetNames::XRDevice; | 
|  | } | 
|  |  | 
|  | const char* XRDevice::checkSessionSupport( | 
|  | const XRSessionCreationOptions& options) const { | 
|  | if (options.exclusive()) { | 
|  | // Validation for exclusive sessions. | 
|  | if (!supports_exclusive_) { | 
|  | return kExclusiveNotSupported; | 
|  | } | 
|  | } else { | 
|  | // Validation for non-exclusive sessions. | 
|  | if (!options.hasOutputContext()) { | 
|  | return kNoOutputContext; | 
|  | } | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | ScriptPromise XRDevice::supportsSession( | 
|  | ScriptState* script_state, | 
|  | const XRSessionCreationOptions& options) const { | 
|  | // Check to see if the device is capable of supporting the requested session | 
|  | // options. Note that reporting support here does not guarantee that creating | 
|  | // a session with those options will succeed, as other external and | 
|  | // time-sensitve factors (focus state, existance of another exclusive session, | 
|  | // etc.) may prevent the creation of a session as well. | 
|  | const char* reject_reason = checkSessionSupport(options); | 
|  | if (reject_reason) { | 
|  | return ScriptPromise::RejectWithDOMException( | 
|  | script_state, DOMException::Create(kNotSupportedError, reject_reason)); | 
|  | } | 
|  |  | 
|  | // If the above checks pass, resolve without a value. Future API iterations | 
|  | // may specify a value to be returned here. | 
|  | ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 
|  | ScriptPromise promise = resolver->Promise(); | 
|  | resolver->Resolve(); | 
|  | return promise; | 
|  | } | 
|  |  | 
|  | ScriptPromise XRDevice::requestSession( | 
|  | ScriptState* script_state, | 
|  | const XRSessionCreationOptions& options) { | 
|  | // Check first to see if the device is capable of supporting the requested | 
|  | // options. | 
|  | const char* reject_reason = checkSessionSupport(options); | 
|  | if (reject_reason) { | 
|  | return ScriptPromise::RejectWithDOMException( | 
|  | script_state, DOMException::Create(kNotSupportedError, reject_reason)); | 
|  | } | 
|  |  | 
|  | // Check if the current page state prevents the requested session from being | 
|  | // created. | 
|  | if (options.exclusive()) { | 
|  | if (frameProvider()->exclusive_session()) { | 
|  | return ScriptPromise::RejectWithDOMException( | 
|  | script_state, | 
|  | DOMException::Create(kInvalidStateError, kActiveExclusiveSession)); | 
|  | } | 
|  |  | 
|  | Document* doc = ToDocumentOrNull(ExecutionContext::From(script_state)); | 
|  | if (!Frame::HasTransientUserActivation(doc ? doc->GetFrame() : nullptr)) { | 
|  | return ScriptPromise::RejectWithDOMException( | 
|  | script_state, | 
|  | DOMException::Create(kInvalidStateError, kRequestNotInUserGesture)); | 
|  | } | 
|  | } | 
|  |  | 
|  | ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 
|  | ScriptPromise promise = resolver->Promise(); | 
|  |  | 
|  | XRPresentationContext* output_context = nullptr; | 
|  | if (options.hasOutputContext()) { | 
|  | output_context = options.outputContext(); | 
|  | } | 
|  |  | 
|  | XRSession* session = new XRSession(this, options.exclusive(), output_context); | 
|  |  | 
|  | if (options.exclusive()) { | 
|  | frameProvider()->BeginExclusiveSession(session, resolver); | 
|  | } else { | 
|  | resolver->Resolve(session); | 
|  | } | 
|  |  | 
|  | return promise; | 
|  | } | 
|  |  | 
|  | // TODO: Forward these calls on to the sessions once they've been implemented. | 
|  | void XRDevice::OnChanged(device::mojom::blink::VRDisplayInfoPtr display_info) { | 
|  | SetXRDisplayInfo(std::move(display_info)); | 
|  | } | 
|  | void XRDevice::OnExitPresent() {} | 
|  | void XRDevice::OnBlur() {} | 
|  | void XRDevice::OnFocus() {} | 
|  | void XRDevice::OnActivate(device::mojom::blink::VRDisplayEventReason, | 
|  | OnActivateCallback on_handled) {} | 
|  | void XRDevice::OnDeactivate(device::mojom::blink::VRDisplayEventReason) {} | 
|  |  | 
|  | XRFrameProvider* XRDevice::frameProvider() { | 
|  | if (!frame_provider_) | 
|  | frame_provider_ = new XRFrameProvider(this); | 
|  |  | 
|  | return frame_provider_; | 
|  | } | 
|  |  | 
|  | void XRDevice::Dispose() { | 
|  | display_client_binding_.Close(); | 
|  | if (frame_provider_) | 
|  | frame_provider_->Dispose(); | 
|  | } | 
|  |  | 
|  | void XRDevice::SetXRDisplayInfo( | 
|  | device::mojom::blink::VRDisplayInfoPtr display_info) { | 
|  | display_info_ = std::move(display_info); | 
|  | is_external_ = display_info_->capabilities->hasExternalDisplay; | 
|  | supports_exclusive_ = (display_info_->capabilities->canPresent); | 
|  | } | 
|  |  | 
|  | void XRDevice::Trace(blink::Visitor* visitor) { | 
|  | visitor->Trace(xr_); | 
|  | visitor->Trace(frame_provider_); | 
|  | EventTargetWithInlineData::Trace(visitor); | 
|  | } | 
|  |  | 
|  | }  // namespace blink |