From c5d637fbb15874caa0d9669ac8cc79912ed4d5d9 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 9 Apr 2022 19:53:17 -0700 Subject: WIP --- .../jsc/bindings/webcore/DOMPromiseProxy.h | 350 +++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h (limited to 'src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h') diff --git a/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h b/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h new file mode 100644 index 000000000..2b8706e76 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2017-2021 Apple Inc. 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. + */ + +#pragma once + +#include "ExceptionOr.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMPromiseDeferred.h" +#include +#include + +namespace WebCore { + +template +class DOMPromiseProxy { + WTF_MAKE_FAST_ALLOCATED; +public: + using Value = typename IDLType::StorageType; + + DOMPromiseProxy() = default; + ~DOMPromiseProxy() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(typename IDLType::StorageType); + void resolveWithNewlyCreated(typename IDLType::StorageType); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + JSC::JSValue resolvePromise(JSC::JSGlobalObject&, JSDOMGlobalObject&, const Function&); + + std::optional> m_valueOrException; + Vector, 1> m_deferredPromises; +}; + +template<> +class DOMPromiseProxy { + WTF_MAKE_FAST_ALLOCATED; +public: + DOMPromiseProxy() = default; + ~DOMPromiseProxy() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + std::optional> m_valueOrException; + Vector, 1> m_deferredPromises; +}; + +// Instead of storing the value of the resolution directly, DOMPromiseProxyWithResolveCallback +// allows the owner to specify callback to be called when the resolved value is needed. This is +// needed to avoid reference cycles when the resolved value is the owner, such as is the case with +// FontFace and FontFaceSet. +template +class DOMPromiseProxyWithResolveCallback { + WTF_MAKE_FAST_ALLOCATED; +public: + using ResolveCallback = Function; + + template + DOMPromiseProxyWithResolveCallback(Class&, typename IDLType::ParameterType (BaseClass::*)()); + DOMPromiseProxyWithResolveCallback(ResolveCallback&&); + ~DOMPromiseProxyWithResolveCallback() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(typename IDLType::ParameterType); + void resolveWithNewlyCreated(typename IDLType::ParameterType); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + ResolveCallback m_resolveCallback; + std::optional> m_valueOrException; + Vector, 1> m_deferredPromises; +}; + +// MARK: - DOMPromiseProxy generic implementation + +template +inline JSC::JSValue DOMPromiseProxy::resolvePromise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const Function& resolvePromiseCallback) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + resolvePromiseCallback(*deferredPromise); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +template +inline JSC::JSValue DOMPromiseProxy::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + return resolvePromise(lexicalGlobalObject, globalObject, [this](auto& deferredPromise) { + deferredPromise.template resolve(m_valueOrException->returnValue()); + }); +} + +template<> +inline JSC::JSValue DOMPromiseProxy::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + return resolvePromise(lexicalGlobalObject, globalObject, [this](auto& deferredPromise) { + deferredPromise.resolveWithJSValue(m_valueOrException->returnValue().get()); + }); +} + +template +inline void DOMPromiseProxy::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +template +inline bool DOMPromiseProxy::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +template +inline void DOMPromiseProxy::resolve(typename IDLType::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { std::forward(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolve(m_valueOrException->returnValue()); +} + +template<> +inline void DOMPromiseProxy::resolve(typename IDLAny::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { std::forward(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->resolveWithJSValue(m_valueOrException->returnValue().get()); +} + +template +inline void DOMPromiseProxy::resolveWithNewlyCreated(typename IDLType::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { std::forward(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolveWithNewlyCreated(m_valueOrException->returnValue()); +} + +template +inline void DOMPromiseProxy::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + + +// MARK: - DOMPromiseProxy specialization + +inline JSC::JSValue DOMPromiseProxy::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + deferredPromise->resolve(); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +inline void DOMPromiseProxy::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +inline bool DOMPromiseProxy::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +inline void DOMPromiseProxy::resolve() +{ + ASSERT(!m_valueOrException); + m_valueOrException = ExceptionOr { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->resolve(); +} + +inline void DOMPromiseProxy::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + m_valueOrException = ExceptionOr { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + +// MARK: - DOMPromiseProxyWithResolveCallback implementation + +template +template +inline DOMPromiseProxyWithResolveCallback::DOMPromiseProxyWithResolveCallback(Class& object, typename IDLType::ParameterType (BaseClass::*function)()) + : m_resolveCallback(std::bind(function, &object)) +{ +} + +template +inline DOMPromiseProxyWithResolveCallback::DOMPromiseProxyWithResolveCallback(ResolveCallback&& function) + : m_resolveCallback(WTFMove(function)) +{ +} + +template +inline JSC::JSValue DOMPromiseProxyWithResolveCallback::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + deferredPromise->template resolve(m_resolveCallback()); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +template +inline void DOMPromiseProxyWithResolveCallback::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +template +inline bool DOMPromiseProxyWithResolveCallback::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +template +inline void DOMPromiseProxyWithResolveCallback::resolve(typename IDLType::ParameterType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolve(value); +} + +template +inline void DOMPromiseProxyWithResolveCallback::resolveWithNewlyCreated(typename IDLType::ParameterType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolveWithNewlyCreated(value); +} + +template +inline void DOMPromiseProxyWithResolveCallback::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + +} -- cgit v1.2.3