LayoutTests/imported/w3c:
[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 "FetchResponseSource.h"
38 #include "FormData.h"
39 #include "HTTPParsers.h"
40 #include "JSBlob.h"
41 #include "JSDOMFormData.h"
42 #include "ReadableStreamSource.h"
43
44 namespace WebCore {
45
46 static RefPtr<Blob> blobFromArrayBuffer(ArrayBuffer*, const String&);
47
48 FetchBody::FetchBody(Ref<Blob>&& blob)
49     : m_type(Type::Blob)
50     , m_mimeType(blob->type())
51     , m_blob(WTFMove(blob))
52 {
53 }
54
55 FetchBody::FetchBody(Ref<DOMFormData>&& formData)
56     : m_type(Type::FormData)
57     , m_mimeType(ASCIILiteral("multipart/form-data"))
58     , m_formData(WTFMove(formData))
59 {
60     // FIXME: Handle the boundary parameter of multipart/form-data MIME type.
61 }
62
63 FetchBody::FetchBody(String&& text)
64     : m_type(Type::Text)
65     , m_mimeType(ASCIILiteral("text/plain;charset=UTF-8"))
66     , m_text(WTFMove(text))
67 {
68 }
69
70 FetchBody FetchBody::extract(JSC::ExecState& state, JSC::JSValue value)
71 {
72     if (value.inherits(JSBlob::info()))
73         return FetchBody(*JSBlob::toWrapped(value));
74     if (value.inherits(JSDOMFormData::info()))
75         return FetchBody(*JSDOMFormData::toWrapped(value));
76     if (value.isString())
77         return FetchBody(value.toWTFString(&state));
78     return { };
79 }
80
81 FetchBody FetchBody::extractFromBody(FetchBody* body)
82 {
83     if (!body)
84         return { };
85
86     return FetchBody(WTFMove(*body));
87 }
88
89 void FetchBody::arrayBuffer(FetchBodyOwner& owner, DeferredWrapper&& promise)
90 {
91     ASSERT(m_type != Type::None);
92     consume(owner, Consumer::Type::ArrayBuffer, WTFMove(promise));
93 }
94
95 void FetchBody::blob(FetchBodyOwner& owner, DeferredWrapper&& promise)
96 {
97     ASSERT(m_type != Type::None);
98     consume(owner, Consumer::Type::Blob, WTFMove(promise));
99 }
100
101 void FetchBody::json(FetchBodyOwner& owner, DeferredWrapper&& promise)
102 {
103     ASSERT(m_type != Type::None);
104
105     if (m_type == Type::Text) {
106         fulfillPromiseWithJSON(promise, m_text);
107         return;
108     }
109     consume(owner, Consumer::Type::JSON, WTFMove(promise));
110 }
111
112 void FetchBody::text(FetchBodyOwner& owner, DeferredWrapper&& promise)
113 {
114     ASSERT(m_type != Type::None);
115
116     if (m_type == Type::Text) {
117         promise.resolve(m_text);
118         return;
119     }
120     consume(owner, Consumer::Type::Text, WTFMove(promise));
121 }
122
123 void FetchBody::consume(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
124 {
125     if (m_type == Type::ArrayBuffer) {
126         consumeArrayBuffer(type, promise);
127         return;
128     }
129     if (m_type == Type::Text) {
130         consumeText(type, promise);
131         return;
132     }
133     if (m_type == Type::Blob) {
134         consumeBlob(owner, type, WTFMove(promise));
135         return;
136     }
137     if (m_type == Type::Loading) {
138         // FIXME: We should be able to change the loading type to text if consumer type is JSON or Text.
139         m_consumer = Consumer({type, WTFMove(promise)});
140         return;
141     }
142
143     // FIXME: Support other types.
144     promise.reject<ExceptionCode>(0);
145 }
146
147 #if ENABLE(STREAMS_API)
148 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
149 {
150     ASSERT(m_type != Type::Loading);
151
152     switch (m_type) {
153     case Type::ArrayBuffer:
154         source.enqueue(m_data);
155         source.close();
156         return;
157     case Type::Text: {
158         Vector<uint8_t> data = extractFromText();
159         // FIXME: We should not close the source if ArrayBuffer;;tryCreate returns null.
160         source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size()));
161         source.close();
162         return;
163     }
164     case Type::Blob:
165         ASSERT(m_blob);
166         owner.loadBlob(*m_blob, FetchLoader::Type::Stream);
167         return;
168     case Type::None:
169         source.close();
170         return;
171     default:
172         source.error(ASCIILiteral("not implemented"));
173     }
174 }
175 #endif
176
177 void FetchBody::consumeArrayBuffer(Consumer::Type type, DeferredWrapper& promise)
178 {
179     if (type == Consumer::Type::ArrayBuffer) {
180         fulfillPromiseWithArrayBuffer(promise, m_data.get());
181         return;
182     }
183     if (type == Consumer::Type::Blob) {
184         promise.resolve(blobFromArrayBuffer(m_data.get(), Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType))));
185         return;
186     }
187
188     ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
189     // FIXME: Do we need TextResourceDecoder to create a String to decode UTF-8 data.
190     fulfillTextPromise(type, TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")->decodeAndFlush(static_cast<const char*>(m_data->data()), m_data->byteLength()), promise);
191 }
192
193 void FetchBody::consumeText(Consumer::Type type, DeferredWrapper& promise)
194 {
195     ASSERT(type == Consumer::Type::ArrayBuffer || type == Consumer::Type::Blob);
196
197     if (type == Consumer::Type::ArrayBuffer) {
198         Vector<uint8_t> data = extractFromText();
199         fulfillPromiseWithArrayBuffer(promise, data.data(), data.size());
200         return;
201     }
202     String contentType = Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType));
203     promise.resolve<RefPtr<Blob>>(Blob::create(extractFromText(), contentType));
204 }
205
206 FetchLoader::Type FetchBody::loadingType(Consumer::Type type)
207 {
208     switch (type) {
209     case Consumer::Type::JSON:
210     case Consumer::Type::Text:
211         return FetchLoader::Type::Text;
212     case Consumer::Type::Blob:
213     case Consumer::Type::ArrayBuffer:
214         return FetchLoader::Type::ArrayBuffer;
215     default:
216         ASSERT_NOT_REACHED();
217         return FetchLoader::Type::ArrayBuffer;
218     };
219 }
220
221 void FetchBody::consumeBlob(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
222 {
223     ASSERT(m_blob);
224
225     m_consumer = Consumer({type, WTFMove(promise)});
226     owner.loadBlob(*m_blob, loadingType(type));
227 }
228
229 Vector<uint8_t> FetchBody::extractFromText() const
230 {
231     ASSERT(m_type == Type::Text);
232     // FIXME: This double allocation is not efficient. Might want to fix that at WTFString level.
233     CString data = m_text.utf8();
234     Vector<uint8_t> value(data.length());
235     memcpy(value.data(), data.data(), data.length());
236     return value;
237 }
238
239 static inline RefPtr<Blob> blobFromArrayBuffer(ArrayBuffer* buffer, const String& contentType)
240 {
241     if (!buffer)
242         return Blob::create(Vector<uint8_t>(), contentType);
243
244     // FIXME: We should try to move buffer to Blob without doing this copy.
245     Vector<uint8_t> value(buffer->byteLength());
246     memcpy(value.data(), buffer->data(), buffer->byteLength());
247     return Blob::create(WTFMove(value), contentType);
248 }
249
250 void FetchBody::fulfillTextPromise(FetchBody::Consumer::Type type, const String& text, DeferredWrapper& promise)
251 {
252     ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
253     if (type == FetchBody::Consumer::Type::Text)
254         promise.resolve(text);
255     else
256         fulfillPromiseWithJSON(promise, text);
257 }
258
259 void FetchBody::loadingFailed()
260 {
261     ASSERT(m_consumer);
262     m_consumer->promise.reject<ExceptionCode>(0);
263     m_consumer = Nullopt;
264 }
265
266 void FetchBody::loadedAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
267 {
268     if (m_type == Type::Loading) {
269         m_type = Type::ArrayBuffer;
270         m_data = buffer;
271         if (m_consumer) {
272             consumeArrayBuffer(m_consumer->type, m_consumer->promise);
273             m_consumer = Nullopt;
274         }
275         return;
276     }
277
278     ASSERT(m_consumer);
279     ASSERT(m_consumer->type == Consumer::Type::Blob || m_consumer->type == Consumer::Type::ArrayBuffer);
280     if (m_consumer->type == Consumer::Type::ArrayBuffer)
281         fulfillPromiseWithArrayBuffer(m_consumer->promise, buffer.get());
282     else {
283         ASSERT(m_blob);
284         m_consumer->promise.resolve(blobFromArrayBuffer(buffer.get(), m_blob->type()));
285     }
286     m_consumer = Nullopt;
287 }
288
289 void FetchBody::loadedAsText(String&& text)
290 {
291     ASSERT(m_consumer);
292     ASSERT(m_consumer->type == Consumer::Type::Text || m_consumer->type == Consumer::Type::JSON);
293
294     fulfillTextPromise(m_consumer->type, text, m_consumer->promise);
295     m_consumer = Nullopt;
296 }
297
298 RefPtr<FormData> FetchBody::bodyForInternalRequest() const
299 {
300     if (m_type == Type::None)
301         return nullptr;
302     if (m_type == Type::Text)
303         return FormData::create(UTF8Encoding().encode(m_text, EntitiesForUnencodables));
304     if (m_type == Type::Blob) {
305         RefPtr<FormData> body = FormData::create();
306         body->appendBlob(m_blob->url());
307         return body;
308     }
309     ASSERT_NOT_REACHED();
310     return nullptr;
311 }
312
313 }
314
315 #endif // ENABLE(FETCH_API)