diff options
Diffstat (limited to 'Source/WebCore/bindings/js/JSDOMPromise.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/JSDOMPromise.cpp | 189 |
1 files changed, 171 insertions, 18 deletions
diff --git a/Source/WebCore/bindings/js/JSDOMPromise.cpp b/Source/WebCore/bindings/js/JSDOMPromise.cpp index fa5aa95b9..188d20fef 100644 --- a/Source/WebCore/bindings/js/JSDOMPromise.cpp +++ b/Source/WebCore/bindings/js/JSDOMPromise.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,47 +26,200 @@ #include "config.h" #include "JSDOMPromise.h" +#include "ExceptionCode.h" +#include "JSDOMError.h" +#include "JSDOMWindow.h" +#include <builtins/BuiltinNames.h> +#include <runtime/Exception.h> +#include <runtime/JSONObject.h> +#include <runtime/JSPromiseConstructor.h> + using namespace JSC; namespace WebCore { -DeferredWrapper::DeferredWrapper(ExecState* exec, JSDOMGlobalObject* globalObject) - : m_globalObject(exec->vm(), globalObject) - , m_deferred(exec->vm(), JSPromiseDeferred::create(exec, globalObject)) +DeferredPromise::DeferredPromise(JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred) + : ActiveDOMCallback(globalObject.scriptExecutionContext()) + , m_deferred(&promiseDeferred) + , m_globalObject(&globalObject) +{ + auto locker = lockDuringMarking(globalObject.vm().heap, globalObject.gcLock()); + globalObject.vm().heap.writeBarrier(&globalObject, &promiseDeferred); + globalObject.deferredPromises(locker).add(this); +} + +DeferredPromise::~DeferredPromise() +{ + clear(); +} + +void DeferredPromise::clear() { + ASSERT(!m_deferred || m_globalObject); + if (m_deferred && m_globalObject) { + auto locker = lockDuringMarking(m_globalObject->vm().heap, m_globalObject->gcLock()); + m_globalObject->deferredPromises(locker).remove(this); + } + m_deferred.clear(); } -JSObject* DeferredWrapper::promise() const +void DeferredPromise::contextDestroyed() { + ActiveDOMCallback::contextDestroyed(); + clear(); +} + +JSC::JSValue DeferredPromise::promise() const +{ + ASSERT(m_deferred); return m_deferred->promise(); } -void DeferredWrapper::resolve(ExecState* exec, JSValue resolution) +void DeferredPromise::callFunction(ExecState& exec, JSValue function, JSValue resolution) { - JSValue deferredResolve = m_deferred->resolve(); + if (!canInvokeCallback()) + return; - CallData resolveCallData; - CallType resolveCallType = getCallData(deferredResolve, resolveCallData); - ASSERT(resolveCallType != CallTypeNone); + CallData callData; + CallType callType = getCallData(function, callData); + ASSERT(callType != CallType::None); MarkedArgumentBuffer arguments; arguments.append(resolution); - call(exec, deferredResolve, resolveCallType, resolveCallData, jsUndefined(), arguments); + call(&exec, function, callType, callData, jsUndefined(), arguments); + + clear(); } -void DeferredWrapper::reject(ExecState* exec, JSValue reason) +void DeferredPromise::reject() { - JSValue deferredReject = m_deferred->reject(); + if (isSuspended()) + return; - CallData rejectCallData; - CallType rejectCallType = getCallData(deferredReject, rejectCallData); - ASSERT(rejectCallType != CallTypeNone); + ASSERT(m_deferred); + ASSERT(m_globalObject); + auto& state = *m_globalObject->globalExec(); + JSC::JSLockHolder locker(&state); + reject(state, JSC::jsUndefined()); +} + +void DeferredPromise::reject(std::nullptr_t) +{ + if (isSuspended()) + return; + + ASSERT(m_deferred); + ASSERT(m_globalObject); + auto& state = *m_globalObject->globalExec(); + JSC::JSLockHolder locker(&state); + reject(state, JSC::jsNull()); +} + +void DeferredPromise::reject(Exception&& exception) +{ + if (isSuspended()) + return; + + ASSERT(m_deferred); + ASSERT(m_globalObject); + auto& state = *m_globalObject->globalExec(); + JSC::JSLockHolder locker(&state); + reject(state, createDOMException(state, WTFMove(exception))); +} + +void DeferredPromise::reject(ExceptionCode ec, const String& message) +{ + if (isSuspended()) + return; + + ASSERT(m_deferred); + ASSERT(m_globalObject); + JSC::ExecState* state = m_globalObject->globalExec(); + JSC::JSLockHolder locker(state); + reject(*state, createDOMException(state, ec, message)); +} + +void DeferredPromise::reject(const JSC::PrivateName& privateName) +{ + if (isSuspended()) + return; + + ASSERT(m_deferred); + ASSERT(m_globalObject); + JSC::ExecState* state = m_globalObject->globalExec(); + JSC::JSLockHolder locker(state); + reject(*state, JSC::Symbol::create(state->vm(), privateName.uid())); +} + +void rejectPromiseWithExceptionIfAny(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred) +{ + VM& vm = state.vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + if (LIKELY(!scope.exception())) + return; + + JSValue error = scope.exception()->value(); + scope.clearException(); + + DeferredPromise::create(globalObject, promiseDeferred)->reject<IDLAny>(error); +} + +Ref<DeferredPromise> createDeferredPromise(JSC::ExecState& state, JSDOMWindow& domWindow) +{ + JSC::JSPromiseDeferred* deferred = JSC::JSPromiseDeferred::create(&state, &domWindow); + // deferred can only be null in workers. + ASSERT(deferred); + return DeferredPromise::create(domWindow, *deferred); +} + +JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage) +{ + ASSERT(state.lexicalGlobalObject()); + auto& globalObject = *state.lexicalGlobalObject(); + + auto promiseConstructor = globalObject.promiseConstructor(); + auto rejectFunction = promiseConstructor->get(&state, state.vm().propertyNames->builtinNames().rejectPrivateName()); + auto rejectionValue = createTypeError(&state, errorMessage); + + CallData callData; + auto callType = getCallData(rejectFunction, callData); + ASSERT(callType != CallType::None); MarkedArgumentBuffer arguments; - arguments.append(reason); + arguments.append(rejectionValue); + + return JSValue::encode(call(&state, rejectFunction, callType, callData, promiseConstructor, arguments)); +} - call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), arguments); +static inline JSC::JSValue parseAsJSON(JSC::ExecState* state, const String& data) +{ + JSC::JSLockHolder lock(state); + return JSC::JSONParse(state, data); +} + +void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data) +{ + JSC::JSValue value = parseAsJSON(promise->globalObject()->globalExec(), data); + if (!value) + promise->reject(SYNTAX_ERR); + else + promise->resolve<IDLAny>(value); +} + +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer) +{ + if (!arrayBuffer) { + promise->reject<IDLAny>(createOutOfMemoryError(promise->globalObject()->globalExec())); + return; + } + promise->resolve<IDLInterface<ArrayBuffer>>(*arrayBuffer); +} + +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length) +{ + fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get()); } } |