[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / Modules / fetch / FetchBody.cpp
1 /*
2  * Copyright (C) 2016 Canon Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted, provided that the following conditions
6  * are required to be met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Canon Inc. nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "FetchBody.h"
31
32 #if ENABLE(FETCH_API)
33
34 #include "DOMRequestState.h"
35 #include "Dictionary.h"
36 #include "FetchBodyOwner.h"
37 #include "FetchHeaders.h"
38 #include "FetchResponseSource.h"
39 #include "FormData.h"
40 #include "HTTPHeaderValues.h"
41 #include "HTTPParsers.h"
42 #include "JSBlob.h"
43 #include "JSDOMFormData.h"
44 #include "JSReadableStream.h"
45 #include "JSURLSearchParams.h"
46 #include "ReadableStreamSource.h"
47 #include <runtime/ArrayBufferView.h>
48
49 namespace WebCore {
50
51 std::optional<FetchBody> FetchBody::extract(ScriptExecutionContext& context, JSC::ExecState& state, JSC::JSValue value, String& contentType)
52 {
53     if (value.inherits(JSBlob::info())) {
54         auto& blob = *JSBlob::toWrapped(value);
55         contentType = blob.type();
56         return FetchBody(blob);
57     }
58     if (value.inherits(JSDOMFormData::info())) {
59         ASSERT(!context.isWorkerGlobalScope());
60         auto& domFormData = *JSDOMFormData::toWrapped(value);
61         auto formData = FormData::createMultiPart(domFormData, domFormData.encoding(), &static_cast<Document&>(context));
62         contentType = makeString("multipart/form-data;boundary=", formData->boundary().data());
63         return FetchBody(WTFMove(formData));
64     }
65     if (value.isString()) {
66         contentType = HTTPHeaderValues::textPlainContentType();
67         return FetchBody(value.toWTFString(&state));
68     }
69     if (value.inherits(JSURLSearchParams::info())) {
70         contentType = HTTPHeaderValues::formURLEncodedContentType();
71         return FetchBody(*JSURLSearchParams::toWrapped(value));
72     }
73     if (value.inherits(JSReadableStream::info()))
74         return FetchBody();
75     if (value.inherits(JSC::JSArrayBuffer::info())) {
76         ArrayBuffer* data = toUnsharedArrayBuffer(value);
77         ASSERT(data);
78         return FetchBody(*data);
79     }
80     if (value.inherits(JSC::JSArrayBufferView::info()))
81         return FetchBody(toUnsharedArrayBufferView(value).releaseConstNonNull());
82
83     return std::nullopt;
84 }
85
86 void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
87 {
88     m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
89     consume(owner, WTFMove(promise));
90 }
91
92 void FetchBody::blob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise, const String& contentType)
93 {
94     m_consumer.setType(FetchBodyConsumer::Type::Blob);
95     m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
96     consume(owner, WTFMove(promise));
97 }
98
99 void FetchBody::json(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
100 {
101     if (isText()) {
102         fulfillPromiseWithJSON(WTFMove(promise), textBody());
103         return;
104     }
105     m_consumer.setType(FetchBodyConsumer::Type::JSON);
106     consume(owner, WTFMove(promise));
107 }
108
109 void FetchBody::text(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
110 {
111     if (isText()) {
112         promise->resolve(textBody());
113         return;
114     }
115     m_consumer.setType(FetchBodyConsumer::Type::Text);
116     consume(owner, WTFMove(promise));
117 }
118
119 void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise)
120 {
121     m_consumer.setType(type);
122     m_consumePromise = WTFMove(promise);
123 }
124
125 void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
126 {
127     if (isArrayBuffer()) {
128         consumeArrayBuffer(WTFMove(promise));
129         return;
130     }
131     if (isArrayBufferView()) {
132         consumeArrayBufferView(WTFMove(promise));
133         return;
134     }
135     if (isText()) {
136         consumeText(WTFMove(promise), textBody());
137         return;
138     }
139     if (isURLSearchParams()) {
140         consumeText(WTFMove(promise), urlSearchParamsBody().toString());
141         return;
142     }
143     if (isBlob()) {
144         consumeBlob(owner, WTFMove(promise));
145         return;
146     }
147     if (isFormData()) {
148         // FIXME: Support consuming FormData.
149         promise->reject(0);
150         return;
151     }
152     m_consumer.resolve(WTFMove(promise));
153 }
154
155 #if ENABLE(READABLE_STREAM_API)
156 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
157 {
158     bool closeStream = false;
159     if (isArrayBuffer()) {
160         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferBody().data(), arrayBufferBody().byteLength()));
161         m_data = nullptr;
162     } else if (isArrayBufferView()) {
163         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength()));
164         m_data = nullptr;
165     } else if (isText()) {
166         auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables);
167         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
168         m_data = nullptr;
169     } else if (isURLSearchParams()) {
170         auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables);
171         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
172         m_data = nullptr;
173     } else if (isBlob()) {
174         owner.loadBlob(blobBody(), nullptr);
175         m_data = nullptr;
176     } else if (isFormData())
177         source.error(ASCIILiteral("not implemented"));
178     else if (m_consumer.hasData())
179         closeStream = source.enqueue(m_consumer.takeAsArrayBuffer());
180     else
181         closeStream = true;
182
183     if (closeStream)
184         source.close();
185 }
186 #endif
187
188 void FetchBody::consumeArrayBuffer(Ref<DeferredPromise>&& promise)
189 {
190     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
191     m_data = nullptr;
192 }
193
194 void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise)
195 {
196     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
197     m_data = nullptr;
198 }
199
200 void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text)
201 {
202     auto data = UTF8Encoding().encode(text, EntitiesForUnencodables);
203     m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast<const uint8_t*>(data.data()), data.length());
204     m_data = nullptr;
205 }
206
207 void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
208 {
209     m_consumePromise = WTFMove(promise);
210     owner.loadBlob(blobBody(), &m_consumer);
211     m_data = nullptr;
212 }
213
214 void FetchBody::loadingFailed()
215 {
216     if (m_consumePromise) {
217         m_consumePromise->reject(0);
218         m_consumePromise = nullptr;
219     }
220 }
221
222 void FetchBody::loadingSucceeded()
223 {
224     if (m_consumePromise)
225         m_consumer.resolve(m_consumePromise.releaseNonNull());
226 }
227
228 RefPtr<FormData> FetchBody::bodyForInternalRequest(ScriptExecutionContext& context) const
229 {
230     if (isText())
231         return FormData::create(UTF8Encoding().encode(textBody(), EntitiesForUnencodables));
232     if (isURLSearchParams())
233         return FormData::create(UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables));
234     if (isBlob()) {
235         RefPtr<FormData> body = FormData::create();
236         body->appendBlob(blobBody().url());
237         return body;
238     }
239     if (isArrayBuffer())
240         return FormData::create(arrayBufferBody().data(), arrayBufferBody().byteLength());
241     if (isArrayBufferView())
242         return FormData::create(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength());
243     if (isFormData()) {
244         ASSERT(!context.isWorkerGlobalScope());
245         RefPtr<FormData> body = const_cast<FormData*>(&formDataBody());
246         body->generateFiles(static_cast<Document*>(&context));
247         return body;
248     }
249     ASSERT_NOT_REACHED();
250     return nullptr;
251 }
252
253 FetchBody FetchBody::clone() const
254 {
255     ASSERT(!m_consumePromise);
256     FetchBody clone(m_consumer);
257
258     if (isArrayBuffer())
259         clone.m_data = arrayBufferBody();
260     else if (isArrayBufferView())
261         clone.m_data = arrayBufferViewBody();
262     else if (isBlob())
263         clone.m_data = blobBody();
264     else if (isFormData())
265         clone.m_data = const_cast<FormData&>(formDataBody());
266     else if (isText())
267         clone.m_data = textBody();
268     else if (isURLSearchParams())
269         clone.m_data = urlSearchParamsBody();
270     return clone;
271 }
272
273 }
274
275 #endif // ENABLE(FETCH_API)