Opaque being-loaded responses should clone their body
[WebKit-https.git] / Source / WebCore / Modules / fetch / FetchBodyOwner.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 "FetchBodyOwner.h"
31
32 #include "FetchLoader.h"
33 #include "HTTPParsers.h"
34 #include "JSBlob.h"
35 #include "ResourceError.h"
36 #include "ResourceResponse.h"
37
38 namespace WebCore {
39
40 FetchBodyOwner::FetchBodyOwner(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers)
41     : ActiveDOMObject(&context)
42     , m_body(WTFMove(body))
43     , m_headers(WTFMove(headers))
44 {
45     suspendIfNeeded();
46 }
47
48 void FetchBodyOwner::stop()
49 {
50     if (m_body)
51         m_body->cleanConsumer();
52
53     if (m_blobLoader) {
54         bool isUniqueReference = hasOneRef();
55         if (m_blobLoader->loader)
56             m_blobLoader->loader->stop();
57         // After that point, 'this' may be destroyed, since unsetPendingActivity should have been called.
58         ASSERT_UNUSED(isUniqueReference, isUniqueReference || !m_blobLoader);
59     }
60 }
61
62 bool FetchBodyOwner::isDisturbed() const
63 {
64     if (isBodyNull())
65         return false;
66
67     if (m_isDisturbed)
68         return true;
69
70 #if ENABLE(STREAMS_API)
71     if (body().readableStream())
72         return body().readableStream()->isDisturbed();
73 #endif
74
75     return false;
76 }
77
78 bool FetchBodyOwner::isDisturbedOrLocked() const
79 {
80     if (isBodyNull())
81         return false;
82
83     if (m_isDisturbed)
84         return true;
85
86 #if ENABLE(STREAMS_API)
87     if (body().readableStream())
88         return body().readableStream()->isDisturbed() || body().readableStream()->isLocked();
89 #endif
90
91     return false;
92 }
93
94 void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise)
95 {
96     if (isBodyNullOrOpaque()) {
97         fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0);
98         return;
99     }
100     if (isDisturbedOrLocked()) {
101         promise->reject(TypeError);
102         return;
103     }
104     m_isDisturbed = true;
105     m_body->arrayBuffer(*this, WTFMove(promise));
106 }
107
108 void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise)
109 {
110     if (isBodyNullOrOpaque()) {
111         promise->resolve<IDLInterface<Blob>>(Blob::create(Vector<uint8_t> { }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))));
112         return;
113     }
114     if (isDisturbedOrLocked()) {
115         promise->reject(TypeError);
116         return;
117     }
118     m_isDisturbed = true;
119     m_body->blob(*this, WTFMove(promise), m_contentType);
120 }
121
122 void FetchBodyOwner::cloneBody(FetchBodyOwner& owner)
123 {
124     m_contentType = owner.m_contentType;
125     if (owner.isBodyNull())
126         return;
127     m_body = owner.m_body->clone();
128 }
129
130 void FetchBodyOwner::extractBody(ScriptExecutionContext& context, FetchBody::Init&& value)
131 {
132     m_body = FetchBody::extract(context, WTFMove(value), m_contentType);
133 }
134
135 void FetchBodyOwner::updateContentType()
136 {
137     String contentType = m_headers->fastGet(HTTPHeaderName::ContentType);
138     if (!contentType.isNull()) {
139         m_contentType = WTFMove(contentType);
140         return;
141     }
142     if (!m_contentType.isNull())
143         m_headers->fastSet(HTTPHeaderName::ContentType, m_contentType);
144 }
145
146 void FetchBodyOwner::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise)
147 {
148     if (isDisturbedOrLocked()) {
149         promise->reject(TypeError);
150         return;
151     }
152     m_isDisturbed = true;
153     m_body->consumeOnceLoadingFinished(type, WTFMove(promise), m_contentType);
154 }
155
156 void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise)
157 {
158     if (isBodyNullOrOpaque()) {
159         promise->reject();
160         return;
161     }
162     if (isDisturbedOrLocked()) {
163         promise->reject(TypeError);
164         return;
165     }
166     m_isDisturbed = true;
167     m_body->formData(*this, WTFMove(promise));
168 }
169
170 void FetchBodyOwner::json(Ref<DeferredPromise>&& promise)
171 {
172     if (isBodyNullOrOpaque()) {
173         promise->reject(SyntaxError);
174         return;
175     }
176     if (isDisturbedOrLocked()) {
177         promise->reject(TypeError);
178         return;
179     }
180     m_isDisturbed = true;
181     m_body->json(*this, WTFMove(promise));
182 }
183
184 void FetchBodyOwner::text(Ref<DeferredPromise>&& promise)
185 {
186     if (isBodyNullOrOpaque()) {
187         promise->resolve<IDLDOMString>({ });
188         return;
189     }
190     if (isDisturbedOrLocked()) {
191         promise->reject(TypeError);
192         return;
193     }
194     m_isDisturbed = true;
195     m_body->text(*this, WTFMove(promise));
196 }
197
198 void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer)
199 {
200     // Can only be called once for a body instance.
201     ASSERT(!m_blobLoader);
202     ASSERT(!isBodyNull());
203
204     if (!scriptExecutionContext()) {
205         m_body->loadingFailed();
206         return;
207     }
208
209     m_blobLoader.emplace(*this);
210     m_blobLoader->loader = std::make_unique<FetchLoader>(*m_blobLoader, consumer);
211
212     m_blobLoader->loader->start(*scriptExecutionContext(), blob);
213     if (!m_blobLoader->loader->isStarted()) {
214         m_body->loadingFailed();
215         m_blobLoader = std::nullopt;
216         return;
217     }
218     setPendingActivity(this);
219 }
220
221 void FetchBodyOwner::finishBlobLoading()
222 {
223     ASSERT(m_blobLoader);
224
225     m_blobLoader = std::nullopt;
226     unsetPendingActivity(this);
227 }
228
229 void FetchBodyOwner::blobLoadingSucceeded()
230 {
231     ASSERT(!isBodyNull());
232 #if ENABLE(STREAMS_API)
233     if (m_readableStreamSource) {
234         m_readableStreamSource->close();
235         m_readableStreamSource = nullptr;
236     }
237 #endif
238     m_body->loadingSucceeded();
239     finishBlobLoading();
240 }
241
242 void FetchBodyOwner::blobLoadingFailed()
243 {
244     ASSERT(!isBodyNull());
245 #if ENABLE(STREAMS_API)
246     if (m_readableStreamSource) {
247         if (!m_readableStreamSource->isCancelling())
248             m_readableStreamSource->error(ASCIILiteral("Blob loading failed"));
249         m_readableStreamSource = nullptr;
250     } else
251 #endif
252         m_body->loadingFailed();
253
254     finishBlobLoading();
255 }
256
257 void FetchBodyOwner::blobChunk(const char* data, size_t size)
258 {
259     ASSERT(data);
260 #if ENABLE(STREAMS_API)
261     ASSERT(m_readableStreamSource);
262     if (!m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size)))
263         stop();
264 #else
265     UNUSED_PARAM(data);
266     UNUSED_PARAM(size);
267 #endif
268 }
269
270 FetchBodyOwner::BlobLoader::BlobLoader(FetchBodyOwner& owner)
271     : owner(owner)
272 {
273 }
274
275 void FetchBodyOwner::BlobLoader::didReceiveResponse(const ResourceResponse& response)
276 {
277     if (response.httpStatusCode() != 200)
278         didFail({ });
279 }
280
281 void FetchBodyOwner::BlobLoader::didFail(const ResourceError&)
282 {
283     // didFail might be called within FetchLoader::start call.
284     if (loader->isStarted())
285         owner.blobLoadingFailed();
286 }
287
288 RefPtr<ReadableStream> FetchBodyOwner::readableStream(JSC::ExecState& state)
289 {
290     if (isBodyNullOrOpaque())
291         return nullptr;
292
293     if (!m_body->hasReadableStream())
294         createReadableStream(state);
295
296     return m_body->readableStream();
297 }
298
299 void FetchBodyOwner::createReadableStream(JSC::ExecState& state)
300 {
301     ASSERT(!m_readableStreamSource);
302     if (isDisturbed()) {
303         m_body->setReadableStream(ReadableStream::create(state, nullptr));
304         m_body->readableStream()->lock();
305     } else {
306         m_readableStreamSource = adoptRef(*new FetchBodySource(*this));
307         m_body->setReadableStream(ReadableStream::create(state, m_readableStreamSource));
308     }
309 }
310
311 void FetchBodyOwner::consumeBodyAsStream()
312 {
313     ASSERT(m_readableStreamSource);
314
315     body().consumeAsStream(*this, *m_readableStreamSource);
316     if (!m_readableStreamSource->isPulling())
317         m_readableStreamSource = nullptr;
318 }
319
320 } // namespace WebCore