1c0597e3190221e1c8c7b475a491d10adaf0973d
[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 "ExceptionCode.h"
37 #include "FetchBodyOwner.h"
38 #include "HTTPParsers.h"
39 #include "JSBlob.h"
40 #include "JSDOMFormData.h"
41
42 namespace WebCore {
43
44 FetchBody::FetchBody(Ref<Blob>&& blob)
45     : m_type(Type::Blob)
46     , m_mimeType(blob->type())
47     , m_blob(WTFMove(blob))
48 {
49 }
50
51 FetchBody::FetchBody(Ref<DOMFormData>&& formData)
52     : m_type(Type::FormData)
53     , m_mimeType(ASCIILiteral("multipart/form-data"))
54     , m_formData(WTFMove(formData))
55 {
56     // FIXME: Handle the boundary parameter of multipart/form-data MIME type.
57 }
58
59 FetchBody::FetchBody(String&& text)
60     : m_type(Type::Text)
61     , m_mimeType(ASCIILiteral("text/plain;charset=UTF-8"))
62     , m_text(WTFMove(text))
63 {
64 }
65
66 FetchBody FetchBody::extract(JSC::ExecState& state, JSC::JSValue value)
67 {
68     if (value.inherits(JSBlob::info()))
69         return FetchBody(*JSBlob::toWrapped(value));
70     if (value.inherits(JSDOMFormData::info()))
71         return FetchBody(*JSDOMFormData::toWrapped(value));
72     if (value.isString())
73         return FetchBody(value.toWTFString(&state));
74     return { };
75 }
76
77 FetchBody FetchBody::extractFromBody(FetchBody* body)
78 {
79     if (!body)
80         return { };
81
82     body->m_isDisturbed = true;
83     return FetchBody(WTFMove(*body));
84 }
85
86 bool FetchBody::processIfEmptyOrDisturbed(Consumer::Type type, DeferredWrapper& promise)
87 {
88     if (m_type == Type::None) {
89         switch (type) {
90         case Consumer::Type::Text:
91             promise.resolve(String());
92             return true;
93         case Consumer::Type::Blob:
94             promise.resolve<RefPtr<Blob>>(Blob::create());
95             return true;
96         case Consumer::Type::JSON:
97             promise.reject<ExceptionCode>(SYNTAX_ERR);
98             return true;
99         case Consumer::Type::ArrayBuffer:
100             fulfillPromiseWithArrayBuffer(promise, nullptr, 0);
101             return true;
102         default:
103             ASSERT_NOT_REACHED();
104             promise.reject<ExceptionCode>(0);
105             return true;
106         };
107     }
108
109     if (m_isDisturbed) {
110         promise.reject<ExceptionCode>(TypeError);
111         return true;
112     }
113     m_isDisturbed = true;
114     return false;
115 }
116
117 void FetchBody::arrayBuffer(FetchBodyOwner& owner, DeferredWrapper&& promise)
118 {
119     if (processIfEmptyOrDisturbed(Consumer::Type::ArrayBuffer, promise))
120         return;
121     consume(owner, Consumer::Type::ArrayBuffer, WTFMove(promise));
122 }
123
124 void FetchBody::blob(FetchBodyOwner& owner, DeferredWrapper&& promise)
125 {
126     if (processIfEmptyOrDisturbed(Consumer::Type::Blob, promise))
127         return;
128
129     consume(owner, Consumer::Type::Blob, WTFMove(promise));
130 }
131
132 void FetchBody::json(FetchBodyOwner& owner, DeferredWrapper&& promise)
133 {
134     if (processIfEmptyOrDisturbed(Consumer::Type::JSON, promise))
135         return;
136
137     if (m_type == Type::Text) {
138         fulfillPromiseWithJSON(promise, m_text);
139         return;
140     }
141     consume(owner, Consumer::Type::JSON, WTFMove(promise));
142 }
143
144 void FetchBody::text(FetchBodyOwner& owner, DeferredWrapper&& promise)
145 {
146     if (processIfEmptyOrDisturbed(Consumer::Type::Text, promise))
147         return;
148
149     if (m_type == Type::Text) {
150         promise.resolve(m_text);
151         return;
152     }
153     consume(owner, Consumer::Type::Text, WTFMove(promise));
154 }
155
156 void FetchBody::consume(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
157 {
158     if (m_type == Type::Text) {
159         consumeText(type, WTFMove(promise));
160         return;
161     }
162     if (m_type == Type::Blob) {
163         consumeBlob(owner, type, WTFMove(promise));
164         return;
165     }
166
167     // FIXME: Support other types.
168     promise.reject<ExceptionCode>(0);
169 }
170
171 void FetchBody::consumeText(Consumer::Type type, DeferredWrapper&& promise)
172 {
173     ASSERT(type == Consumer::Type::ArrayBuffer || type == Consumer::Type::Blob);
174
175     if (type == Consumer::Type::ArrayBuffer) {
176         Vector<char> data = extractFromText();
177         fulfillPromiseWithArrayBuffer(promise, data.data(), data.size());
178         return;
179     }
180     String contentType = Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType));
181     promise.resolve<RefPtr<Blob>>(Blob::create(extractFromText(), contentType));
182 }
183
184 FetchLoader::Type FetchBody::loadingType(Consumer::Type type)
185 {
186     switch (type) {
187     case Consumer::Type::JSON:
188     case Consumer::Type::Text:
189         return FetchLoader::Type::Text;
190     case Consumer::Type::Blob:
191     case Consumer::Type::ArrayBuffer:
192         return FetchLoader::Type::ArrayBuffer;
193     default:
194         ASSERT_NOT_REACHED();
195         return FetchLoader::Type::ArrayBuffer;
196     };
197 }
198
199 void FetchBody::consumeBlob(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
200 {
201     ASSERT(m_blob);
202
203     m_consumer = Consumer({type, WTFMove(promise)});
204     owner.loadBlob(*m_blob, loadingType(type));
205 }
206
207 Vector<char> FetchBody::extractFromText() const
208 {
209     ASSERT(m_type == Type::Text);
210     // FIXME: This double allocation is not efficient. Might want to fix that at WTFString level.
211     CString data = m_text.utf8();
212     Vector<char> value(data.length());
213     memcpy(value.data(), data.data(), data.length());
214     return value;
215 }
216
217 void FetchBody::loadingFailed()
218 {
219     ASSERT(m_consumer);
220     m_consumer->promise.reject<ExceptionCode>(0);
221     m_consumer = Nullopt;
222 }
223
224 void FetchBody::loadedAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
225 {
226     ASSERT(m_consumer);
227     ASSERT(m_consumer->type == Consumer::Type::Blob || m_consumer->type == Consumer::Type::ArrayBuffer);
228     if (m_consumer->type == Consumer::Type::ArrayBuffer)
229         fulfillPromiseWithArrayBuffer(m_consumer->promise, buffer.get());
230     else {
231         ASSERT(m_blob);
232         Vector<char> data;
233         data.reserveCapacity(buffer->byteLength());
234         data.append(static_cast<const char*>(buffer->data()), buffer->byteLength());
235         m_consumer->promise.resolve<RefPtr<Blob>>(Blob::create(WTFMove(data), m_blob->type()));
236     }
237     m_consumer = Nullopt;
238 }
239
240 void FetchBody::loadedAsText(String&& text)
241 {
242     ASSERT(m_consumer);
243     ASSERT(m_consumer->type == Consumer::Type::Text || m_consumer->type == Consumer::Type::JSON);
244     if (m_consumer->type == Consumer::Type::Text)
245         m_consumer->promise.resolve(text);
246     else
247         fulfillPromiseWithJSON(m_consumer->promise, text);
248     m_consumer = Nullopt;
249 }
250
251 }
252
253 #endif // ENABLE(FETCH_API)