| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "modules/fetch/Body.h" | 6 #include "modules/fetch/Body.h" |
| 7 | 7 |
| 8 #include "bindings/core/v8/ExceptionState.h" | 8 #include "bindings/core/v8/ExceptionState.h" |
| 9 #include "bindings/core/v8/ScriptPromiseResolver.h" | 9 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| 10 #include "bindings/core/v8/ScriptState.h" | 10 #include "bindings/core/v8/ScriptState.h" |
| 11 #include "bindings/core/v8/V8ArrayBuffer.h" | 11 #include "bindings/core/v8/V8ArrayBuffer.h" |
| 12 #include "bindings/core/v8/V8ThrowException.h" | 12 #include "bindings/core/v8/V8ThrowException.h" |
| 13 #include "core/dom/DOMArrayBuffer.h" | 13 #include "core/dom/DOMArrayBuffer.h" |
| 14 #include "core/dom/DOMTypedArray.h" | 14 #include "core/dom/DOMTypedArray.h" |
| 15 #include "core/dom/ExceptionCode.h" | 15 #include "core/dom/ExceptionCode.h" |
| 16 #include "core/fileapi/Blob.h" | 16 #include "core/fileapi/Blob.h" |
| 17 #include "core/frame/UseCounter.h" | 17 #include "core/frame/UseCounter.h" |
| 18 #include "core/streams/ReadableByteStream.h" | |
| 19 #include "core/streams/ReadableByteStreamReader.h" | |
| 20 #include "core/streams/UnderlyingSource.h" | |
| 21 #include "modules/fetch/BodyStreamBuffer.h" | 18 #include "modules/fetch/BodyStreamBuffer.h" |
| 22 #include "modules/fetch/DataConsumerHandleUtil.h" | 19 #include "modules/fetch/FetchDataLoader.h" |
| 23 #include "modules/fetch/FetchBlobDataConsumerHandle.h" | 20 #include "wtf/PassRefPtr.h" |
| 21 #include "wtf/RefPtr.h" |
| 24 | 22 |
| 25 namespace blink { | 23 namespace blink { |
| 26 | 24 |
| 27 class Body::ReadableStreamSource : public GarbageCollectedFinalized<Body::Readab
leStreamSource>, public UnderlyingSource, public WebDataConsumerHandle::Client,
public BodyStreamBuffer::DrainingStreamNotificationClient { | 25 namespace { |
| 28 USING_GARBAGE_COLLECTED_MIXIN(ReadableStreamSource); | 26 |
| 27 class BodyConsumerBase : public GarbageCollectedFinalized<BodyConsumerBase>, pub
lic FetchDataLoader::Client { |
| 28 WTF_MAKE_NONCOPYABLE(BodyConsumerBase); |
| 29 USING_GARBAGE_COLLECTED_MIXIN(BodyConsumerBase); |
| 29 public: | 30 public: |
| 30 ReadableStreamSource(ExecutionContext* executionContext, BodyStreamBuffer* b
uffer) | 31 explicit BodyConsumerBase(PassRefPtrWillBeRawPtr<ScriptPromiseResolver> reso
lver) : m_resolver(resolver) {} |
| 31 : m_bodyStreamBuffer(buffer) | 32 PassRefPtrWillBeRawPtr<ScriptPromiseResolver> resolver() { return m_resolver
; } |
| 32 , m_streamNeedsMore(false) | 33 void didFetchDataLoadFailed() override |
| 33 #if ENABLE(ASSERT) | |
| 34 , m_drained(false) | |
| 35 , m_isCloseCalled(false) | |
| 36 , m_isErrorCalled(false) | |
| 37 #endif | |
| 38 { | 34 { |
| 39 if (m_bodyStreamBuffer) | 35 ScriptState::Scope scope(resolver()->scriptState()); |
| 40 obtainReader(); | 36 m_resolver->reject(V8ThrowException::createTypeError(resolver()->scriptS
tate()->isolate(), "Failed to fetch")); |
| 41 } | 37 } |
| 42 | 38 |
| 43 ~ReadableStreamSource() override { } | 39 DEFINE_INLINE_TRACE() |
| 44 | |
| 45 void startStream(ReadableByteStream* stream) | |
| 46 { | 40 { |
| 47 m_stream = stream; | 41 visitor->trace(m_resolver); |
| 48 stream->didSourceStart(); | 42 FetchDataLoader::Client::trace(visitor); |
| 49 if (!m_bodyStreamBuffer) | |
| 50 close(); | |
| 51 } | |
| 52 // Creates a new BodyStreamBuffer to drain the data. | |
| 53 PassOwnPtr<DrainingBodyStreamBuffer> createDrainingStream() | |
| 54 { | |
| 55 if (!m_bodyStreamBuffer) | |
| 56 return nullptr; | |
| 57 | |
| 58 #if ENABLE(ASSERT) | |
| 59 ASSERT(!m_drained); | |
| 60 m_drained = true; | |
| 61 ASSERT(!(m_stream->stateInternal() == ReadableByteStream::Closed && !m_i
sCloseCalled)); | |
| 62 ASSERT(!(m_stream->stateInternal() == ReadableByteStream::Errored && !m_
isErrorCalled)); | |
| 63 #endif | |
| 64 | |
| 65 m_reader.clear(); | |
| 66 return DrainingBodyStreamBuffer::create(m_bodyStreamBuffer, this); | |
| 67 } | |
| 68 | |
| 69 DEFINE_INLINE_VIRTUAL_TRACE() | |
| 70 { | |
| 71 visitor->trace(m_bodyStreamBuffer); | |
| 72 visitor->trace(m_stream); | |
| 73 UnderlyingSource::trace(visitor); | |
| 74 DrainingStreamNotificationClient::trace(visitor); | |
| 75 } | |
| 76 | |
| 77 void close() | |
| 78 { | |
| 79 m_reader.clear(); | |
| 80 m_stream->close(); | |
| 81 if (m_bodyStreamBuffer) | |
| 82 m_bodyStreamBuffer = BodyStreamBuffer::createEmpty(); | |
| 83 #if ENABLE(ASSERT) | |
| 84 m_isCloseCalled = true; | |
| 85 #endif | |
| 86 } | |
| 87 | |
| 88 void error() | |
| 89 { | |
| 90 m_reader.clear(); | |
| 91 m_stream->error(DOMException::create(NetworkError, "network error")); | |
| 92 if (m_bodyStreamBuffer) | |
| 93 m_bodyStreamBuffer = BodyStreamBuffer::create(createFetchDataConsume
rHandleFromWebHandle(createUnexpectedErrorDataConsumerHandle())); | |
| 94 #if ENABLE(ASSERT) | |
| 95 m_isErrorCalled = true; | |
| 96 #endif | |
| 97 } | 43 } |
| 98 | 44 |
| 99 private: | 45 private: |
| 100 void obtainReader() | 46 RefPtrWillBeMember<ScriptPromiseResolver> m_resolver; |
| 101 { | |
| 102 m_reader = m_bodyStreamBuffer->handle()->obtainReader(this); | |
| 103 } | |
| 104 | |
| 105 void didFetchDataLoadFinishedFromDrainingStream() | |
| 106 { | |
| 107 ASSERT(m_bodyStreamBuffer); | |
| 108 ASSERT(m_drained); | |
| 109 | |
| 110 #if ENABLE(ASSERT) | |
| 111 m_drained = false; | |
| 112 #endif | |
| 113 obtainReader(); | |
| 114 // We have to call didGetReadable() now to call close()/error() if | |
| 115 // necessary. | |
| 116 // didGetReadable() would be called asynchronously, but it is too late. | |
| 117 didGetReadable(); | |
| 118 } | |
| 119 | |
| 120 void didGetReadable() override | |
| 121 { | |
| 122 if (!m_streamNeedsMore) { | |
| 123 // Perform zero-length read to call close()/error() early. | |
| 124 size_t readSize; | |
| 125 WebDataConsumerHandle::Result result = m_reader->read(nullptr, 0, We
bDataConsumerHandle::FlagNone, &readSize); | |
| 126 switch (result) { | |
| 127 case WebDataConsumerHandle::Ok: | |
| 128 case WebDataConsumerHandle::ShouldWait: | |
| 129 return; | |
| 130 case WebDataConsumerHandle::Done: | |
| 131 close(); | |
| 132 return; | |
| 133 case WebDataConsumerHandle::Busy: | |
| 134 case WebDataConsumerHandle::ResourceExhausted: | |
| 135 case WebDataConsumerHandle::UnexpectedError: | |
| 136 error(); | |
| 137 return; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 processData(); | |
| 142 } | |
| 143 | |
| 144 // UnderlyingSource functions. | |
| 145 void pullSource() override | |
| 146 { | |
| 147 ASSERT(!m_streamNeedsMore); | |
| 148 m_streamNeedsMore = true; | |
| 149 | |
| 150 ASSERT(!m_drained); | |
| 151 | |
| 152 processData(); | |
| 153 } | |
| 154 | |
| 155 ScriptPromise cancelSource(ScriptState* scriptState, ScriptValue reason) ove
rride | |
| 156 { | |
| 157 close(); | |
| 158 return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isola
te())); | |
| 159 } | |
| 160 | |
| 161 // Reads data and writes the data to |m_stream|, as long as data are | |
| 162 // available and the stream has pending reads. | |
| 163 void processData() | |
| 164 { | |
| 165 ASSERT(m_reader); | |
| 166 while (m_streamNeedsMore) { | |
| 167 const void* buffer; | |
| 168 size_t available; | |
| 169 WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer,
WebDataConsumerHandle::FlagNone, &available); | |
| 170 switch (result) { | |
| 171 case WebDataConsumerHandle::Ok: | |
| 172 m_streamNeedsMore = m_stream->enqueue(DOMUint8Array::create(stat
ic_cast<const unsigned char*>(buffer), available)); | |
| 173 m_reader->endRead(available); | |
| 174 break; | |
| 175 | |
| 176 case WebDataConsumerHandle::Done: | |
| 177 close(); | |
| 178 return; | |
| 179 | |
| 180 case WebDataConsumerHandle::ShouldWait: | |
| 181 return; | |
| 182 | |
| 183 case WebDataConsumerHandle::Busy: | |
| 184 case WebDataConsumerHandle::ResourceExhausted: | |
| 185 case WebDataConsumerHandle::UnexpectedError: | |
| 186 error(); | |
| 187 return; | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // Source of data. | |
| 193 Member<BodyStreamBuffer> m_bodyStreamBuffer; | |
| 194 OwnPtr<FetchDataConsumerHandle::Reader> m_reader; | |
| 195 | |
| 196 Member<ReadableByteStream> m_stream; | |
| 197 bool m_streamNeedsMore; | |
| 198 #if ENABLE(ASSERT) | |
| 199 bool m_drained; | |
| 200 bool m_isCloseCalled; | |
| 201 bool m_isErrorCalled; | |
| 202 #endif | |
| 203 }; | 47 }; |
| 204 | 48 |
| 205 ScriptPromise Body::readAsync(ScriptState* scriptState, ResponseType type) | 49 class BodyBlobConsumer final : public BodyConsumerBase { |
| 50 WTF_MAKE_NONCOPYABLE(BodyBlobConsumer); |
| 51 public: |
| 52 explicit BodyBlobConsumer(PassRefPtrWillBeRawPtr<ScriptPromiseResolver> reso
lver) : BodyConsumerBase(resolver) {} |
| 53 |
| 54 void didFetchDataLoadedBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle)
override |
| 55 { |
| 56 resolver()->resolve(Blob::create(blobDataHandle)); |
| 57 } |
| 58 }; |
| 59 |
| 60 class BodyArrayBufferConsumer final : public BodyConsumerBase { |
| 61 WTF_MAKE_NONCOPYABLE(BodyArrayBufferConsumer); |
| 62 public: |
| 63 explicit BodyArrayBufferConsumer(PassRefPtrWillBeRawPtr<ScriptPromiseResolve
r> resolver) : BodyConsumerBase(resolver) {} |
| 64 |
| 65 void didFetchDataLoadedArrayBuffer(PassRefPtr<DOMArrayBuffer> arrayBuffer) o
verride |
| 66 { |
| 67 resolver()->resolve(arrayBuffer); |
| 68 } |
| 69 }; |
| 70 |
| 71 class BodyTextConsumer final : public BodyConsumerBase { |
| 72 WTF_MAKE_NONCOPYABLE(BodyTextConsumer); |
| 73 public: |
| 74 explicit BodyTextConsumer(PassRefPtrWillBeRawPtr<ScriptPromiseResolver> reso
lver) : BodyConsumerBase(resolver) {} |
| 75 |
| 76 void didFetchDataLoadedString(const String& string) override |
| 77 { |
| 78 resolver()->resolve(string); |
| 79 } |
| 80 }; |
| 81 |
| 82 class BodyJsonConsumer final : public BodyConsumerBase { |
| 83 WTF_MAKE_NONCOPYABLE(BodyJsonConsumer); |
| 84 public: |
| 85 explicit BodyJsonConsumer(PassRefPtrWillBeRawPtr<ScriptPromiseResolver> reso
lver) : BodyConsumerBase(resolver) {} |
| 86 |
| 87 void didFetchDataLoadedString(const String& string) override |
| 88 { |
| 89 if (!resolver()->executionContext() || resolver()->executionContext()->a
ctiveDOMObjectsAreStopped()) |
| 90 return; |
| 91 ScriptState::Scope scope(resolver()->scriptState()); |
| 92 v8::Isolate* isolate = resolver()->scriptState()->isolate(); |
| 93 v8::Local<v8::String> inputString = v8String(isolate, string); |
| 94 v8::TryCatch trycatch; |
| 95 v8::Local<v8::Value> parsed; |
| 96 if (v8Call(v8::JSON::Parse(isolate, inputString), parsed, trycatch)) |
| 97 resolver()->resolve(parsed); |
| 98 else |
| 99 resolver()->reject(trycatch.Exception()); |
| 100 } |
| 101 }; |
| 102 |
| 103 } // namespace |
| 104 |
| 105 ScriptPromise Body::arrayBuffer(ScriptState* scriptState) |
| 206 { | 106 { |
| 207 if (bodyUsed()) | 107 if (bodyUsed()) |
| 208 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "Already read")); | 108 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "Already read")); |
| 209 | 109 |
| 210 // When the main thread sends a V8::TerminateExecution() signal to a worker | 110 // When the main thread sends a V8::TerminateExecution() signal to a worker |
| 211 // thread, any V8 API on the worker thread starts returning an empty | 111 // thread, any V8 API on the worker thread starts returning an empty |
| 212 // handle. This can happen in Body::readAsync. To avoid the situation, we | 112 // handle. This can happen in Body::readAsync. To avoid the situation, we |
| 213 // first check the ExecutionContext and return immediately if it's already | 113 // first check the ExecutionContext and return immediately if it's already |
| 214 // gone (which means that the V8::TerminateExecution() signal has been sent | 114 // gone (which means that the V8::TerminateExecution() signal has been sent |
| 215 // to this worker thread). | 115 // to this worker thread). |
| 216 ExecutionContext* executionContext = scriptState->executionContext(); | 116 if (!scriptState->executionContext()) |
| 217 if (!executionContext) | |
| 218 return ScriptPromise(); | 117 return ScriptPromise(); |
| 219 | 118 |
| 220 lockBody(); | 119 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 221 m_responseType = type; | 120 ScriptPromise promise = resolver->promise(); |
| 222 | 121 bodyBuffer()->startLoading(scriptState->executionContext(), FetchDataLoader:
:createLoaderAsArrayBuffer(), new BodyArrayBufferConsumer(resolver)); |
| 223 ASSERT(!m_resolver); | |
| 224 m_resolver = ScriptPromiseResolver::create(scriptState); | |
| 225 ScriptPromise promise = m_resolver->promise(); | |
| 226 | |
| 227 if (m_stream->stateInternal() == ReadableStream::Closed) { | |
| 228 resolveWithEmptyDataSynchronously(); | |
| 229 } else if (m_stream->stateInternal() == ReadableStream::Errored) { | |
| 230 m_resolver->reject(m_stream->storedException()); | |
| 231 m_resolver.clear(); | |
| 232 } else { | |
| 233 readAsyncFromDrainingBodyStreamBuffer(createDrainingStream(), mimeType()
); | |
| 234 } | |
| 235 return promise; | 122 return promise; |
| 236 } | 123 } |
| 237 | 124 |
| 238 void Body::resolveWithEmptyDataSynchronously() | |
| 239 { | |
| 240 // We resolve the resolver manually in order not to use member | |
| 241 // variables. | |
| 242 switch (m_responseType) { | |
| 243 case ResponseAsArrayBuffer: | |
| 244 m_resolver->resolve(DOMArrayBuffer::create(nullptr, 0)); | |
| 245 break; | |
| 246 case ResponseAsBlob: { | |
| 247 OwnPtr<BlobData> blobData = BlobData::create(); | |
| 248 blobData->setContentType(mimeType()); | |
| 249 m_resolver->resolve(Blob::create(BlobDataHandle::create(blobData.release
(), 0))); | |
| 250 break; | |
| 251 } | |
| 252 case ResponseAsText: | |
| 253 m_resolver->resolve(String()); | |
| 254 break; | |
| 255 case ResponseAsFormData: | |
| 256 // TODO(yhirano): Implement this. | |
| 257 ASSERT_NOT_REACHED(); | |
| 258 break; | |
| 259 case ResponseAsJSON: { | |
| 260 ScriptState::Scope scope(m_resolver->scriptState()); | |
| 261 m_resolver->reject(V8ThrowException::createSyntaxError(m_resolver->scrip
tState()->isolate(), "Unexpected end of input")); | |
| 262 break; | |
| 263 } | |
| 264 case ResponseUnknown: | |
| 265 ASSERT_NOT_REACHED(); | |
| 266 break; | |
| 267 } | |
| 268 m_resolver.clear(); | |
| 269 } | |
| 270 | |
| 271 void Body::readAsyncFromDrainingBodyStreamBuffer(PassOwnPtr<DrainingBodyStreamBu
ffer> buffer, const String& mimeType) | |
| 272 { | |
| 273 if (!buffer) { | |
| 274 resolveWithEmptyDataSynchronously(); | |
| 275 m_streamSource->close(); | |
| 276 return; | |
| 277 } | |
| 278 | |
| 279 FetchDataLoader* fetchDataLoader = nullptr; | |
| 280 | |
| 281 switch (m_responseType) { | |
| 282 case ResponseAsArrayBuffer: | |
| 283 fetchDataLoader = FetchDataLoader::createLoaderAsArrayBuffer(); | |
| 284 break; | |
| 285 | |
| 286 case ResponseAsJSON: | |
| 287 case ResponseAsText: | |
| 288 fetchDataLoader = FetchDataLoader::createLoaderAsString(); | |
| 289 break; | |
| 290 | |
| 291 case ResponseAsBlob: | |
| 292 fetchDataLoader = FetchDataLoader::createLoaderAsBlobHandle(mimeType); | |
| 293 break; | |
| 294 | |
| 295 case ResponseAsFormData: | |
| 296 // FIXME: Implement this. | |
| 297 ASSERT_NOT_REACHED(); | |
| 298 return; | |
| 299 | |
| 300 default: | |
| 301 ASSERT_NOT_REACHED(); | |
| 302 return; | |
| 303 } | |
| 304 | |
| 305 buffer->startLoading(fetchDataLoader, this); | |
| 306 } | |
| 307 | |
| 308 ScriptPromise Body::arrayBuffer(ScriptState* scriptState) | |
| 309 { | |
| 310 return readAsync(scriptState, ResponseAsArrayBuffer); | |
| 311 } | |
| 312 | |
| 313 ScriptPromise Body::blob(ScriptState* scriptState) | 125 ScriptPromise Body::blob(ScriptState* scriptState) |
| 314 { | 126 { |
| 315 return readAsync(scriptState, ResponseAsBlob); | 127 if (bodyUsed()) |
| 316 } | 128 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "Already read")); |
| 317 | 129 |
| 318 ScriptPromise Body::formData(ScriptState* scriptState) | 130 // See above comment. |
| 319 { | 131 if (!scriptState->executionContext()) |
| 320 return readAsync(scriptState, ResponseAsFormData); | 132 return ScriptPromise(); |
| 133 |
| 134 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 135 ScriptPromise promise = resolver->promise(); |
| 136 bodyBuffer()->startLoading(scriptState->executionContext(), FetchDataLoader:
:createLoaderAsBlobHandle(mimeType()), new BodyBlobConsumer(resolver)); |
| 137 return promise; |
| 138 |
| 321 } | 139 } |
| 322 | 140 |
| 323 ScriptPromise Body::json(ScriptState* scriptState) | 141 ScriptPromise Body::json(ScriptState* scriptState) |
| 324 { | 142 { |
| 325 return readAsync(scriptState, ResponseAsJSON); | 143 if (bodyUsed()) |
| 144 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "Already read")); |
| 145 |
| 146 // See above comment. |
| 147 if (!scriptState->executionContext()) |
| 148 return ScriptPromise(); |
| 149 |
| 150 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 151 ScriptPromise promise = resolver->promise(); |
| 152 bodyBuffer()->startLoading(scriptState->executionContext(), FetchDataLoader:
:createLoaderAsString(), new BodyJsonConsumer(resolver)); |
| 153 return promise; |
| 326 } | 154 } |
| 327 | 155 |
| 328 ScriptPromise Body::text(ScriptState* scriptState) | 156 ScriptPromise Body::text(ScriptState* scriptState) |
| 329 { | 157 { |
| 330 return readAsync(scriptState, ResponseAsText); | 158 if (bodyUsed()) |
| 159 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "Already read")); |
| 160 |
| 161 // See above comment. |
| 162 if (!scriptState->executionContext()) |
| 163 return ScriptPromise(); |
| 164 |
| 165 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 166 ScriptPromise promise = resolver->promise(); |
| 167 bodyBuffer()->startLoading(scriptState->executionContext(), FetchDataLoader:
:createLoaderAsString(), new BodyTextConsumer(resolver)); |
| 168 return promise; |
| 331 } | 169 } |
| 332 | 170 |
| 333 ReadableByteStream* Body::body() | 171 ReadableByteStream* Body::body() |
| 334 { | 172 { |
| 335 UseCounter::count(executionContext(), UseCounter::FetchBodyStream); | 173 UseCounter::count(executionContext(), UseCounter::FetchBodyStream); |
| 336 return m_stream; | 174 return bodyBuffer()->stream(); |
| 337 } | 175 } |
| 338 | 176 |
| 339 bool Body::bodyUsed() const | 177 bool Body::bodyUsed() |
| 340 { | 178 { |
| 341 return m_bodyUsed || m_stream->isLocked(); | 179 return m_bodyPassed || body()->isLocked(); |
| 342 } | |
| 343 | |
| 344 void Body::lockBody(LockBodyOption option) | |
| 345 { | |
| 346 ASSERT(!bodyUsed()); | |
| 347 if (option == PassBody) | |
| 348 m_bodyUsed = true; | |
| 349 ASSERT(!m_stream->isLocked()); | |
| 350 TrackExceptionState exceptionState; | |
| 351 m_stream->getBytesReader(executionContext(), exceptionState); | |
| 352 ASSERT(!exceptionState.hadException()); | |
| 353 } | |
| 354 | |
| 355 void Body::setBody(BodyStreamBuffer* buffer) | |
| 356 { | |
| 357 m_streamSource = new ReadableStreamSource(executionContext(), buffer); | |
| 358 m_stream = new ReadableByteStream(m_streamSource, new ReadableByteStream::St
rictStrategy); | |
| 359 m_streamSource->startStream(m_stream); | |
| 360 } | |
| 361 | |
| 362 PassOwnPtr<DrainingBodyStreamBuffer> Body::createDrainingStream() | |
| 363 { | |
| 364 return m_streamSource->createDrainingStream(); | |
| 365 } | 180 } |
| 366 | 181 |
| 367 bool Body::hasPendingActivity() const | 182 bool Body::hasPendingActivity() const |
| 368 { | 183 { |
| 369 if (executionContext()->activeDOMObjectsAreStopped()) | 184 if (executionContext()->activeDOMObjectsAreStopped()) |
| 370 return false; | 185 return false; |
| 371 if (m_resolver) | 186 return bodyBuffer()->hasPendingActivity(); |
| 372 return true; | |
| 373 if (m_stream->isLocked()) | |
| 374 return true; | |
| 375 return false; | |
| 376 } | 187 } |
| 377 | 188 |
| 378 DEFINE_TRACE(Body) | 189 Body::Body(ExecutionContext* context) : ActiveDOMObject(context), m_bodyPassed(f
alse) |
| 379 { | 190 { |
| 380 visitor->trace(m_resolver); | 191 suspendIfNeeded(); |
| 381 visitor->trace(m_stream); | |
| 382 visitor->trace(m_streamSource); | |
| 383 ActiveDOMObject::trace(visitor); | |
| 384 FetchDataLoader::Client::trace(visitor); | |
| 385 } | |
| 386 | |
| 387 Body::Body(ExecutionContext* context) | |
| 388 : ActiveDOMObject(context) | |
| 389 , m_bodyUsed(false) | |
| 390 , m_responseType(ResponseType::ResponseUnknown) | |
| 391 , m_streamSource(new ReadableStreamSource(context, nullptr)) | |
| 392 , m_stream(new ReadableByteStream(m_streamSource, new ReadableByteStream::St
rictStrategy)) | |
| 393 { | |
| 394 m_streamSource->startStream(m_stream); | |
| 395 } | |
| 396 | |
| 397 void Body::resolveJSON(const String& string) | |
| 398 { | |
| 399 ASSERT(m_responseType == ResponseAsJSON); | |
| 400 ScriptState::Scope scope(m_resolver->scriptState()); | |
| 401 v8::Isolate* isolate = m_resolver->scriptState()->isolate(); | |
| 402 v8::Local<v8::String> inputString = v8String(isolate, string); | |
| 403 v8::TryCatch trycatch; | |
| 404 v8::Local<v8::Value> parsed; | |
| 405 if (v8Call(v8::JSON::Parse(isolate, inputString), parsed, trycatch)) | |
| 406 m_resolver->resolve(parsed); | |
| 407 else | |
| 408 m_resolver->reject(trycatch.Exception()); | |
| 409 } | |
| 410 | |
| 411 // FetchDataLoader::Client functions. | |
| 412 void Body::didFetchDataLoadFailed() | |
| 413 { | |
| 414 if (!executionContext() || executionContext()->activeDOMObjectsAreStopped()) | |
| 415 return; | |
| 416 | |
| 417 if (m_resolver) { | |
| 418 if (!m_resolver->executionContext() || m_resolver->executionContext()->a
ctiveDOMObjectsAreStopped()) { | |
| 419 m_resolver.clear(); | |
| 420 return; | |
| 421 } | |
| 422 ScriptState* state = m_resolver->scriptState(); | |
| 423 ScriptState::Scope scope(state); | |
| 424 m_resolver->reject(V8ThrowException::createTypeError(state->isolate(), "
Failed to fetch")); | |
| 425 m_resolver.clear(); | |
| 426 } | |
| 427 } | |
| 428 | |
| 429 void Body::didFetchDataLoadedBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandl
e) | |
| 430 { | |
| 431 if (!executionContext() || executionContext()->activeDOMObjectsAreStopped()) | |
| 432 return; | |
| 433 | |
| 434 ASSERT(m_responseType == ResponseAsBlob); | |
| 435 m_resolver->resolve(Blob::create(blobDataHandle)); | |
| 436 m_resolver.clear(); | |
| 437 } | |
| 438 | |
| 439 void Body::didFetchDataLoadedArrayBuffer(PassRefPtr<DOMArrayBuffer> arrayBuffer) | |
| 440 { | |
| 441 if (!executionContext() || executionContext()->activeDOMObjectsAreStopped()) | |
| 442 return; | |
| 443 | |
| 444 ASSERT(m_responseType == ResponseAsArrayBuffer); | |
| 445 m_resolver->resolve(arrayBuffer); | |
| 446 m_resolver.clear(); | |
| 447 } | |
| 448 | |
| 449 void Body::didFetchDataLoadedString(const String& str) | |
| 450 { | |
| 451 if (!executionContext() || executionContext()->activeDOMObjectsAreStopped()) | |
| 452 return; | |
| 453 | |
| 454 switch (m_responseType) { | |
| 455 case ResponseAsJSON: | |
| 456 resolveJSON(str); | |
| 457 break; | |
| 458 case ResponseAsText: | |
| 459 m_resolver->resolve(str); | |
| 460 break; | |
| 461 default: | |
| 462 ASSERT_NOT_REACHED(); | |
| 463 } | |
| 464 | |
| 465 m_resolver.clear(); | |
| 466 } | 192 } |
| 467 | 193 |
| 468 } // namespace blink | 194 } // namespace blink |
| OLD | NEW |