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