[WebIDL] Add support for converting dictionaries to JS
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromise.cpp
1 /*
2  * Copyright (C) 2013, 2016 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()
93 {
94     if (isSuspended())
95         return;
96
97     ASSERT(m_deferred);
98     ASSERT(m_globalObject);
99     auto& state = *m_globalObject->globalExec();
100     JSC::JSLockHolder locker(&state);
101     reject(state, JSC::jsUndefined());
102 }
103
104 void DeferredPromise::reject(std::nullptr_t)
105 {
106     if (isSuspended())
107         return;
108
109     ASSERT(m_deferred);
110     ASSERT(m_globalObject);
111     auto& state = *m_globalObject->globalExec();
112     JSC::JSLockHolder locker(&state);
113     reject(state, JSC::jsNull());
114 }
115
116 void DeferredPromise::reject(Exception&& exception)
117 {
118     if (isSuspended())
119         return;
120
121     ASSERT(m_deferred);
122     ASSERT(m_globalObject);
123     auto& state = *m_globalObject->globalExec();
124     JSC::JSLockHolder locker(&state);
125     reject(state, createDOMException(state, WTFMove(exception)));
126 }
127
128 void DeferredPromise::reject(ExceptionCode ec, const String& message)
129 {
130     if (isSuspended())
131         return;
132
133     ASSERT(m_deferred);
134     ASSERT(m_globalObject);
135     JSC::ExecState* state = m_globalObject->globalExec();
136     JSC::JSLockHolder locker(state);
137     reject(*state, createDOMException(state, ec, message));
138 }
139
140 void DeferredPromise::reject(const JSC::PrivateName& privateName)
141 {
142     if (isSuspended())
143         return;
144
145     ASSERT(m_deferred);
146     ASSERT(m_globalObject);
147     JSC::ExecState* state = m_globalObject->globalExec();
148     JSC::JSLockHolder locker(state);
149     reject(*state, toJS(state, m_globalObject.get(), privateName));
150 }
151
152 void rejectPromiseWithExceptionIfAny(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred)
153 {
154     VM& vm = state.vm();
155     auto scope = DECLARE_CATCH_SCOPE(vm);
156
157     if (LIKELY(!scope.exception()))
158         return;
159
160     JSValue error = scope.exception()->value();
161     scope.clearException();
162
163     DeferredPromise::create(globalObject, promiseDeferred)->reject<IDLAny>(error);
164 }
165
166 Ref<DeferredPromise> createDeferredPromise(JSC::ExecState& state, JSDOMWindow& domWindow)
167 {
168     JSC::JSPromiseDeferred* deferred = JSC::JSPromiseDeferred::create(&state, &domWindow);
169     // deferred can only be null in workers.
170     ASSERT(deferred);
171     return DeferredPromise::create(domWindow, *deferred);
172 }
173
174 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage)
175 {
176     ASSERT(state.lexicalGlobalObject());
177     auto& globalObject = *state.lexicalGlobalObject();
178
179     auto promiseConstructor = globalObject.promiseConstructor();
180     auto rejectFunction = promiseConstructor->get(&state, state.vm().propertyNames->builtinNames().rejectPrivateName());
181     auto rejectionValue = createTypeError(&state, errorMessage);
182
183     CallData callData;
184     auto callType = getCallData(rejectFunction, callData);
185     ASSERT(callType != CallType::None);
186
187     MarkedArgumentBuffer arguments;
188     arguments.append(rejectionValue);
189
190     return JSValue::encode(call(&state, rejectFunction, callType, callData, promiseConstructor, arguments));
191 }
192
193 static inline JSC::JSValue parseAsJSON(JSC::ExecState* state, const String& data)
194 {
195     JSC::JSLockHolder lock(state);
196     return JSC::JSONParse(state, data);
197 }
198
199 void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data)
200 {
201     JSC::JSValue value = parseAsJSON(promise->globalObject()->globalExec(), data);
202     if (!value)
203         promise->reject(SYNTAX_ERR);
204     else
205         promise->resolve<IDLAny>(value);
206 }
207
208 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer)
209 {
210     if (!arrayBuffer) {
211         promise->reject<IDLAny>(createOutOfMemoryError(promise->globalObject()->globalExec()));
212         return;
213     }
214     promise->resolve<IDLInterface<ArrayBuffer>>(*arrayBuffer);
215 }
216
217 void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length)
218 {
219     fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get());
220 }
221
222 }