/* * Copyright (C) 2013 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 "JSDOMConvert.h" #include "JSDOMGuardedObject.h" #include #include namespace WebCore { class DeferredPromise : public DOMGuarded { public: enum class Mode { ClearPromiseOnResolve, RetainPromiseOnResolve }; static RefPtr create(JSC::ExecState& state, JSDOMGlobalObject& globalObject, Mode mode = Mode::ClearPromiseOnResolve) { auto* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject); if (!promiseDeferred) return nullptr; return adoptRef(new DeferredPromise(globalObject, *promiseDeferred, mode)); } static Ref create(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode = Mode::ClearPromiseOnResolve) { return adoptRef(*new DeferredPromise(globalObject, deferred, mode)); } template void resolve(typename IDLType::ParameterType value) { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); resolve(*exec, toJS(*exec, *globalObject(), std::forward(value))); } void resolve() { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); resolve(*exec, JSC::jsUndefined()); } template void resolveWithNewlyCreated(typename IDLType::ParameterType value) { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); resolve(*exec, toJSNewlyCreated(*exec, *globalObject(), std::forward(value))); } template void reject(typename IDLType::ParameterType value) { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); reject(*exec, toJS(*exec, *globalObject(), std::forward(value))); } void reject(); void reject(std::nullptr_t); void reject(Exception); WEBCORE_EXPORT void reject(ExceptionCode, const String& = { }); void reject(const JSC::PrivateName&); template void resolveWithCallback(Callback callback) { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); resolve(*exec, callback(*exec, *globalObject())); } template void rejectWithCallback(Callback callback) { if (isSuspended()) return; ASSERT(deferred()); ASSERT(globalObject()); JSC::ExecState* exec = globalObject()->globalExec(); JSC::JSLockHolder locker(exec); reject(*exec, callback(*exec, *globalObject())); } JSC::JSValue promise() const; private: DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromiseDeferred& deferred, Mode mode) : DOMGuarded(globalObject, deferred) , m_mode(mode) { } JSC::JSPromiseDeferred* deferred() const { return guarded(); } WEBCORE_EXPORT void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution); void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->resolve(), resolution); } void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, deferred()->reject(), resolution); } Mode m_mode; }; class DOMPromiseDeferredBase { public: DOMPromiseDeferredBase(Ref&& genericPromise) : m_promiseDeferred(WTFMove(genericPromise)) { } DOMPromiseDeferredBase(DOMPromiseDeferredBase&& promise) : m_promiseDeferred(WTFMove(promise.m_promiseDeferred)) { } DOMPromiseDeferredBase(const DOMPromiseDeferredBase& other) : m_promiseDeferred(other.m_promiseDeferred.copyRef()) { } DOMPromiseDeferredBase& operator=(const DOMPromiseDeferredBase& other) { m_promiseDeferred = other.m_promiseDeferred.copyRef(); return *this; } DOMPromiseDeferredBase& operator=(DOMPromiseDeferredBase&& other) { m_promiseDeferred = WTFMove(other.m_promiseDeferred); return *this; } void reject() { m_promiseDeferred->reject(); } template void reject(ErrorType&&... error) { m_promiseDeferred->reject(std::forward(error)...); } template void rejectType(typename IDLType::ParameterType value) { m_promiseDeferred->reject(std::forward(value)); } JSC::JSValue promise() const { return m_promiseDeferred->promise(); }; protected: Ref m_promiseDeferred; }; template class DOMPromiseDeferred : public DOMPromiseDeferredBase { public: using DOMPromiseDeferredBase::DOMPromiseDeferredBase; using DOMPromiseDeferredBase::operator=; using DOMPromiseDeferredBase::promise; using DOMPromiseDeferredBase::reject; void resolve(typename IDLType::ParameterType value) { m_promiseDeferred->resolve(std::forward(value)); } void settle(ExceptionOr&& result) { if (result.hasException()) { reject(result.releaseException()); return; } resolve(result.releaseReturnValue()); } }; template<> class DOMPromiseDeferred : public DOMPromiseDeferredBase { public: using DOMPromiseDeferredBase::DOMPromiseDeferredBase; using DOMPromiseDeferredBase::operator=; using DOMPromiseDeferredBase::promise; using DOMPromiseDeferredBase::reject; void resolve() { m_promiseDeferred->resolve(); } void settle(ExceptionOr&& result) { if (result.hasException()) { reject(result.releaseException()); return; } resolve(); } }; Ref createDeferredPromise(JSC::ExecState&, JSDOMWindow&); void fulfillPromiseWithJSON(Ref&&, const String&); void fulfillPromiseWithArrayBuffer(Ref&&, ArrayBuffer*); void fulfillPromiseWithArrayBuffer(Ref&&, const void*, size_t); WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&); JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&); using PromiseFunction = void(JSC::ExecState&, Ref&&); enum class PromiseExecutionScope { WindowOnly, WindowOrWorker }; template inline JSC::JSValue callPromiseFunction(JSC::ExecState& state) { JSC::VM& vm = state.vm(); auto scope = DECLARE_CATCH_SCOPE(vm); JSDOMGlobalObject& globalObject = *JSC::jsCast(state.lexicalGlobalObject()); JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject); // promiseDeferred can be null when terminating a Worker abruptly. if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred) return JSC::jsUndefined(); promiseFunction(state, DeferredPromise::create(globalObject, *promiseDeferred)); rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred); ASSERT_UNUSED(scope, !scope.exception()); return promiseDeferred->promise(); } template inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, PromiseFunctor functor) { JSC::VM& vm = state.vm(); auto scope = DECLARE_CATCH_SCOPE(vm); JSDOMGlobalObject& globalObject = *JSC::jsCast(state.lexicalGlobalObject()); JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, &globalObject); // promiseDeferred can be null when terminating a Worker abruptly. if (executionScope == PromiseExecutionScope::WindowOrWorker && !promiseDeferred) return JSC::jsUndefined(); functor(state, DeferredPromise::create(globalObject, *promiseDeferred)); rejectPromiseWithExceptionIfAny(state, globalObject, *promiseDeferred); ASSERT_UNUSED(scope, !scope.exception()); return promiseDeferred->promise(); } using BindingPromiseFunction = JSC::EncodedJSValue(JSC::ExecState*, Ref&&); template inline void bindingPromiseFunctionAdapter(JSC::ExecState& state, Ref&& promise) { bindingFunction(&state, WTFMove(promise)); } template inline JSC::JSValue callPromiseFunction(JSC::ExecState& state) { return callPromiseFunction, executionScope>(state); } } // namespace WebCore