| 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 | 
|---|