[Fetch API] Response bodyUsed should check for its body disturbed state
[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 #if ENABLE(FETCH_API)
33
34 #include "ExceptionCode.h"
35 #include "FetchLoader.h"
36 #include "FetchResponseSource.h"
37 #include "HTTPParsers.h"
38 #include "JSBlob.h"
39 #include "ResourceResponse.h"
40
41 namespace WebCore {
42
43 FetchBodyOwner::FetchBodyOwner(ScriptExecutionContext& context, FetchBody&& body)
44     : ActiveDOMObject(&context)
45     , m_body(WTFMove(body))
46 {
47     suspendIfNeeded();
48 }
49
50 void FetchBodyOwner::stop()
51 {
52     m_body.cleanConsumePromise();
53
54     if (m_blobLoader) {
55         if (m_blobLoader->loader)
56             m_blobLoader->loader->stop();
57     }
58     ASSERT(!m_blobLoader);
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(DeferredWrapper&& promise)
75 {
76     if (m_body.isEmpty()) {
77         fulfillPromiseWithArrayBuffer(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(DeferredWrapper&& promise)
89 {
90     if (m_body.isEmpty()) {
91         promise.resolve(Blob::create(Vector<uint8_t>(), Blob::normalizedContentType(extractMIMETypeFromMediaType(m_body.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));
100 }
101
102 void FetchBodyOwner::formData(DeferredWrapper&& promise)
103 {
104     if (m_body.isEmpty()) {
105         promise.reject(0);
106         return;
107     }
108     if (isDisturbedOrLocked()) {
109         promise.reject(TypeError);
110         return;
111     }
112     m_isDisturbed = true;
113     m_body.formData(*this, WTFMove(promise));
114 }
115
116 void FetchBodyOwner::json(DeferredWrapper&& promise)
117 {
118     if (m_body.isEmpty()) {
119         promise.reject(SYNTAX_ERR);
120         return;
121     }
122     if (isDisturbedOrLocked()) {
123         promise.reject(TypeError);
124         return;
125     }
126     m_isDisturbed = true;
127     m_body.json(*this, WTFMove(promise));
128 }
129
130 void FetchBodyOwner::text(DeferredWrapper&& promise)
131 {
132     if (m_body.isEmpty()) {
133         promise.resolve(String());
134         return;
135     }
136     if (isDisturbedOrLocked()) {
137         promise.reject(TypeError);
138         return;
139     }
140     m_isDisturbed = true;
141     m_body.text(*this, WTFMove(promise));
142 }
143
144 void FetchBodyOwner::loadBlob(Blob& blob, FetchBodyConsumer* consumer)
145 {
146     // Can only be called once for a body instance.
147     ASSERT(isDisturbed());
148     ASSERT(!m_blobLoader);
149
150     if (!scriptExecutionContext()) {
151         m_body.loadingFailed();
152         return;
153     }
154
155     m_blobLoader = { *this };
156     m_blobLoader->loader = std::make_unique<FetchLoader>(*m_blobLoader, consumer);
157
158     m_blobLoader->loader->start(*scriptExecutionContext(), blob);
159     if (!m_blobLoader->loader->isStarted()) {
160         m_body.loadingFailed();
161         m_blobLoader = Nullopt;
162         return;
163     }
164     setPendingActivity(this);
165 }
166
167 void FetchBodyOwner::finishBlobLoading()
168 {
169     ASSERT(m_blobLoader);
170
171     m_blobLoader = Nullopt;
172     unsetPendingActivity(this);
173 }
174
175 void FetchBodyOwner::blobLoadingSucceeded()
176 {
177     ASSERT(m_body.type() == FetchBody::Type::Blob);
178
179 #if ENABLE(STREAMS_API)
180     if (m_readableStreamSource) {
181         m_readableStreamSource->close();
182         m_readableStreamSource = nullptr;
183     }
184 #endif
185     m_body.loadingSucceeded();
186     finishBlobLoading();
187 }
188
189 void FetchBodyOwner::blobLoadingFailed()
190 {
191 #if ENABLE(STREAMS_API)
192     if (m_readableStreamSource) {
193         if (!m_readableStreamSource->isCancelling())
194             m_readableStreamSource->error(ASCIILiteral("Blob loading failed"));
195         m_readableStreamSource = nullptr;
196     } else
197 #endif
198         m_body.loadingFailed();
199
200     finishBlobLoading();
201 }
202
203 void FetchBodyOwner::blobChunk(const char* data, size_t size)
204 {
205     ASSERT(data);
206 #if ENABLE(STREAMS_API)
207     ASSERT(m_readableStreamSource);
208     if (!m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size)))
209         stop();
210 #else
211     UNUSED_PARAM(data);
212     UNUSED_PARAM(size);
213 #endif
214 }
215
216 FetchBodyOwner::BlobLoader::BlobLoader(FetchBodyOwner& owner)
217     : owner(owner)
218 {
219 }
220
221 void FetchBodyOwner::BlobLoader::didReceiveResponse(const ResourceResponse& response)
222 {
223     if (response.httpStatusCode() != 200)
224         didFail();
225 }
226
227 void FetchBodyOwner::BlobLoader::didFail()
228 {
229     // didFail might be called within FetchLoader::start call.
230     if (loader->isStarted())
231         owner.blobLoadingFailed();
232 }
233
234 } // namespace WebCore
235
236 #endif // ENABLE(FETCH_API)