| /* |
| * Copyright (C) 2020 Igalia S.L. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "WebXRInputSourceArray.h" |
| |
| #if ENABLE(WEBXR) |
| #include "EventNames.h" |
| #include "WebXRInputSource.h" |
| #include "WebXRSession.h" |
| #include "XRInputSourceEvent.h" |
| #include "XRInputSourcesChangeEvent.h" |
| #include <wtf/IsoMallocInlines.h> |
| |
| namespace WebCore { |
| |
| WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRInputSourceArray); |
| |
| UniqueRef<WebXRInputSourceArray> WebXRInputSourceArray::create(WebXRSession& session) |
| { |
| return makeUniqueRef<WebXRInputSourceArray>(session); |
| } |
| |
| WebXRInputSourceArray::WebXRInputSourceArray(WebXRSession& session) |
| : m_session(session) |
| { |
| } |
| |
| WebXRInputSourceArray::~WebXRInputSourceArray() = default; |
| |
| void WebXRInputSourceArray::ref() |
| { |
| m_session.ref(); |
| } |
| |
| void WebXRInputSourceArray::deref() |
| { |
| m_session.deref(); |
| } |
| |
| unsigned WebXRInputSourceArray::length() const |
| { |
| return m_inputSources.size(); |
| } |
| |
| WebXRInputSource* WebXRInputSourceArray::item(unsigned index) const |
| { |
| return index >= m_inputSources.size() ? nullptr: m_inputSources[index].ptr(); |
| } |
| |
| void WebXRInputSourceArray::clear() |
| { |
| m_inputSources.clear(); |
| } |
| |
| // https://immersive-web.github.io/webxr/#list-of-active-xr-input-sources |
| void WebXRInputSourceArray::update(double timestamp, const InputSourceList& inputSources) |
| { |
| Vector<RefPtr<WebXRInputSource>> added; |
| Vector<RefPtr<WebXRInputSource>> removed; |
| Vector<Ref<XRInputSourceEvent>> inputEvents; |
| |
| handleRemovedInputSources(inputSources, removed, inputEvents); |
| handleAddedOrUpdatedInputSources(timestamp, inputSources, added, removed, inputEvents); |
| |
| if (!added.isEmpty() || !removed.isEmpty()) { |
| // A user agent MUST dispatch an inputsourceschange event on an XRSession when the session’s list of active XR input sources has changed. |
| XRInputSourcesChangeEvent::Init init; |
| init.session = &m_session; |
| init.added = WTFMove(added); |
| init.removed = WTFMove(removed); |
| |
| auto event = XRInputSourcesChangeEvent::create(eventNames().inputsourceschangeEvent, init); |
| ActiveDOMObject::queueTaskToDispatchEvent(m_session, TaskSource::WebXR, WTFMove(event)); |
| } |
| |
| if (!inputEvents.isEmpty()) { |
| // When the user agent has to fire an input source event with name name, XRFrame frame, and XRInputSource source it MUST run the following steps: |
| // 1. Create an XRInputSourceEvent event with type name, frame frame, and inputSource source. |
| // 2. Set frame’s active boolean to true. |
| // 3. Apply frame updates for frame. |
| // 4. Dispatch event on frame’s session |
| // 5. Set frame’s active boolean to false. |
| |
| for (auto& event : inputEvents) { |
| ActiveDOMObject::queueTaskKeepingObjectAlive(m_session, TaskSource::WebXR, [session = Ref { m_session }, event = WTFMove(event)]() { |
| event->setFrameActive(true); |
| session->dispatchEvent(event.copyRef()); |
| event->setFrameActive(false); |
| }); |
| } |
| } |
| } |
| |
| // https://immersive-web.github.io/webxr/#list-of-active-xr-input-sources |
| void WebXRInputSourceArray::handleRemovedInputSources(const InputSourceList& inputSources, Vector<RefPtr<WebXRInputSource>>& removed, Vector<Ref<XRInputSourceEvent>>& inputEvents) |
| { |
| // When any previously added XR input sources are no longer available for XRSession session, the user agent MUST run the following steps: |
| // 1. If session's promise resolved flag is not set, abort these steps. |
| // 2. Let removed be a new list. |
| // 3. For each XR input source that is no longer available: |
| // 3.1 Let inputSource be the XRInputSource in session's list of active XR input sources associated with the XR input source. |
| // 3.2 Add inputSource to removed. |
| m_inputSources.removeAllMatching([&inputSources, &removed, &inputEvents](auto& source) { |
| if (!WTF::anyOf(inputSources, [&source](auto& item) { return item.handle == source->handle(); })) { |
| removed.append(source.copyRef()); |
| source->disconnect(); |
| source->pollEvents(inputEvents); |
| return true; |
| } |
| return false; |
| }); |
| } |
| |
| // https://immersive-web.github.io/webxr/#list-of-active-xr-input-sources |
| void WebXRInputSourceArray::handleAddedOrUpdatedInputSources(double timestamp, const InputSourceList& inputSources, Vector<RefPtr<WebXRInputSource>>& added, Vector<RefPtr<WebXRInputSource>>& removed, Vector<Ref<XRInputSourceEvent>>& inputEvents) |
| { |
| auto* document = downcast<Document>(m_session.scriptExecutionContext()); |
| if (!document) |
| return; |
| |
| for (auto& inputSource : inputSources) { |
| auto index = m_inputSources.findMatching([&inputSource](auto& item) { return item->handle() == inputSource.handle; }); |
| if (index == notFound) { |
| // When new XR input sources become available for XRSession session, the user agent MUST run the following steps: |
| // 1. If session's promise resolved flag is not set, abort these steps. |
| // 2. Let added be a new list. |
| // 3. For each new XR input source: |
| // 3.1 Let inputSource be a new XRInputSource in the relevant realm of this XRSession. |
| // 3.2 Add inputSource to added. |
| |
| auto input = WebXRInputSource::create(*document, m_session, timestamp, inputSource); |
| added.append(input.copyRef()); |
| input->pollEvents(inputEvents); |
| m_inputSources.append(WTFMove(input)); |
| continue; |
| } |
| |
| // When the handedness, targetRayMode, profiles, or presence of a gripSpace for any XR input sources change for XRSession session, the user agent MUST run the following steps |
| // 1. If session’s promise resolved flag is not set, abort these steps. |
| // 2. Let added be a new list. |
| // 3. Let removed be a new list. |
| // 4. For each changed XR input source: |
| // 4.1 Let oldInputSource be the XRInputSource in session's list of active XR input sources previously associated with the XR input source. |
| // 4.1 Let newInputSource be a new XRInputSource in the relevant realm of session. |
| // 4.1 Add oldInputSource to removed. |
| // 4.1 Add newInputSource to added. |
| auto& input = m_inputSources[index]; |
| |
| if (input->requiresInputSourceChange(inputSource)) { |
| removed.append(input.copyRef()); |
| input->disconnect(); |
| input->pollEvents(inputEvents); |
| m_inputSources.remove(index); |
| |
| auto newInputSource = WebXRInputSource::create(*document, m_session, timestamp, inputSource); |
| added.append(newInputSource.copyRef()); |
| newInputSource->pollEvents(inputEvents); |
| m_inputSources.append(WTFMove(newInputSource)); |
| } else { |
| input->update(timestamp, inputSource); |
| input->pollEvents(inputEvents); |
| } |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(WEBXR) |
| |