Improve DeferredWrapper code
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromise.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSDOMPromise.h"
28
29 #include "ExceptionCode.h"
30 #include "JSDOMError.h"
31 #include "JSDOMWindow.h"
32 #include <builtins/BuiltinNames.h>
33 #include <runtime/Exception.h>
34 #include <runtime/JSONObject.h>
35 #include <runtime/JSPromiseConstructor.h>
36
37 using namespace JSC;
38
39 namespace WebCore {
40
41 DeferredPromise::DeferredPromise(JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred)
42     : ActiveDOMCallback(globalObject.scriptExecutionContext())
43     , m_deferred(&promiseDeferred)
44     , m_globalObject(&globalObject)
45 {
46     globalObject.vm().heap.writeBarrier(&globalObject, &promiseDeferred);
47     globalObject.deferredPromises().add(this);
48 }
49
50 DeferredPromise::~DeferredPromise()
51 {
52     clear();
53 }
54
55 void DeferredPromise::clear()
56 {
57     ASSERT(!m_deferred || m_globalObject);
58     if (m_deferred && m_globalObject)
59         m_globalObject->deferredPromises().remove(this);
60     m_deferred.clear();
61 }
62
63 void DeferredPromise::contextDestroyed()
64 {
65     ActiveDOMCallback::contextDestroyed();
66     clear();
67 }
68
69 JSC::JSValue DeferredPromise::promise() const
70 {
71     ASSERT(m_deferred);
72     return m_deferred->promise();
73 }
74
75 void DeferredPromise::callFunction(ExecState& exec, JSValue function, JSValue resolution)
76 {
77     if (!canInvokeCallback())
78         return;
79
80     CallData callData;
81     CallType callType = getCallData(function, callData);
82     ASSERT(callType != CallType::None);
83
84     MarkedArgumentBuffer arguments;
85     arguments.append(resolution);
86
87     call(&exec, function, callType, callData, jsUndefined(), arguments);
88
89     clear();
90 }
91
92 void DeferredPromise::reject(ExceptionCode ec, const String& message)
93 {
94     if (isSuspended())
95         return;
96
97     ASSERT(m_deferred);
98     ASSERT(m_globalObject);
99     JSC::ExecState* state = m_globalObject->globalExec();
100     JSC::JSLockHolder locker(state);
101     reject(*state, createDOMException(state, ec, message));
102 }
103
104 void rejectPromiseWithExceptionIfAny(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred)
105 {
106     VM& vm = state.vm();
107     auto scope = DECLARE_CATCH_SCOPE(vm);
108
109     if (!scope.exception())
110         return;
111
112     JSValue error = scope.exception()->value();
113     scope.clearException();
114
115     DeferredPromise::create(globalObject, promiseDeferred)->reject(error);
116 }
117
118 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState& state, JSDOMWindow& domWindow)
119 {
120     JSC::JSPromiseDeferred* deferred = JSC::JSPromiseDeferred::create(&state, &domWindow);
121     // deferred can only be null in workers.
122     ASSERT(deferred);
123     return DeferredPromise::create(domWindow, *deferred);
124 }
125
126 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage)
127 {
128     ASSERT(state.lexicalGlobalObject());
129     auto& globalObject = *state.lexicalGlobalObject();
130
131     auto promiseConstructor = globalObject.promiseConstructor();
132     auto rejectFunction = promiseConstructor->get(&state, state.vm().propertyNames->builtinNames().rejectPrivateName());
133     auto rejectionValue = createTypeError(&state, errorMessage);
134
135     CallData callData;
136     auto callType = getCallData(rejectFunction, callData);
137     ASSERT(callType != CallType::None);
138
139     MarkedArgumentBuffer arguments;
140     arguments.append(rejectionValue);
141
142     return JSValue::encode(call(&state, rejectFunction, callType, callData, promiseConstructor, arguments));
143 }
144
145 static inline JSC::JSValue parseAsJSON(JSC::ExecState* state, const String& data)
146 {
147     JSC::JSLockHolder lock(state);
148     return JSC::JSONParse(state, data);
149 }
150
151 void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data)
152 {
153     JSC::JSValue value = parseAsJSON(promise->globalObject()->globalExec(), data);
154     if (!value)
155         promise->reject(SYNTAX_ERR);
156     else
157         promise->resolve(value);
158 }
159
160 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer)
161 {
162     if (!arrayBuffer) {
163         promise->reject<JSValue>(createOutOfMemoryError(promise->globalObject()->globalExec()));
164         return;
165     }
166     promise->resolve(arrayBuffer);
167 }
168
169 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length)
170 {
171     fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get());
172 }
173
174 }