| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef ReadableStreamImpl_h | |
| 6 #define ReadableStreamImpl_h | |
| 7 | |
| 8 #include "bindings/core/v8/ExceptionState.h" | |
| 9 #include "bindings/core/v8/ScriptPromise.h" | |
| 10 #include "bindings/core/v8/ScriptPromiseResolver.h" | |
| 11 #include "bindings/core/v8/ScriptState.h" | |
| 12 #include "bindings/core/v8/ScriptValue.h" | |
| 13 #include "bindings/core/v8/ToV8.h" | |
| 14 #include "bindings/core/v8/V8ArrayBuffer.h" | |
| 15 #include "bindings/core/v8/V8Binding.h" | |
| 16 #include "bindings/core/v8/V8IteratorResultValue.h" | |
| 17 #include "core/dom/DOMArrayBuffer.h" | |
| 18 #include "core/dom/DOMArrayBufferView.h" | |
| 19 #include "core/dom/DOMException.h" | |
| 20 #include "core/streams/ReadableStream.h" | |
| 21 #include "wtf/Deque.h" | |
| 22 #include "wtf/RefPtr.h" | |
| 23 #include "wtf/text/WTFString.h" | |
| 24 #include <utility> | |
| 25 | |
| 26 namespace blink { | |
| 27 | |
| 28 // We define the default ChunkTypeTraits for frequently used types. | |
| 29 template<typename ChunkType> | |
| 30 class ReadableStreamChunkTypeTraits { | |
| 31 STATIC_ONLY(ReadableStreamChunkTypeTraits); | |
| 32 }; | |
| 33 | |
| 34 template<> | |
| 35 class ReadableStreamChunkTypeTraits<String> { | |
| 36 public: | |
| 37 typedef String HoldType; | |
| 38 typedef const String& PassType; | |
| 39 | |
| 40 static size_t size(const String& chunk) { return chunk.length(); } | |
| 41 static ScriptValue toScriptValue(ScriptState* scriptState, const HoldType& v
alue) | |
| 42 { | |
| 43 return ScriptValue(scriptState, v8String(scriptState->isolate(), value))
; | |
| 44 } | |
| 45 }; | |
| 46 | |
| 47 template<> | |
| 48 class ReadableStreamChunkTypeTraits<DOMArrayBuffer> { | |
| 49 public: | |
| 50 typedef DOMArrayBuffer* HoldType; | |
| 51 typedef DOMArrayBuffer* PassType; | |
| 52 | |
| 53 static size_t size(const PassType& chunk) { return chunk->byteLength(); } | |
| 54 static ScriptValue toScriptValue(ScriptState* scriptState, const HoldType& v
alue) | |
| 55 { | |
| 56 return ScriptValue(scriptState, toV8(value, scriptState->context()->Glob
al(), scriptState->isolate())); | |
| 57 } | |
| 58 }; | |
| 59 | |
| 60 template<> | |
| 61 class ReadableStreamChunkTypeTraits<DOMArrayBufferView> { | |
| 62 public: | |
| 63 typedef DOMArrayBufferView* HoldType; | |
| 64 typedef DOMArrayBufferView* PassType; | |
| 65 | |
| 66 static size_t size(const PassType& chunk) { return chunk->byteLength(); } | |
| 67 static ScriptValue toScriptValue(ScriptState* scriptState, const HoldType& v
alue) | |
| 68 { | |
| 69 return ScriptValue(scriptState, toV8(value, scriptState->context()->Glob
al(), scriptState->isolate())); | |
| 70 } | |
| 71 }; | |
| 72 | |
| 73 // ReadableStreamImpl<ChunkTypeTraits> is a ReadableStream subtype. It has a | |
| 74 // queue whose type depends on ChunkTypeTraits and it implements queue-related | |
| 75 // ReadableStream pure virtual methods. | |
| 76 template <typename ChunkTypeTraits> | |
| 77 class ReadableStreamImpl : public ReadableStream { | |
| 78 public: | |
| 79 class Strategy : public GarbageCollectedFinalized<Strategy> { | |
| 80 public: | |
| 81 virtual ~Strategy() { } | |
| 82 | |
| 83 // These functions call ReadableStream::error on error. | |
| 84 virtual size_t size(const typename ChunkTypeTraits::PassType& chunk, Rea
dableStream*) { return ChunkTypeTraits::size(chunk); } | |
| 85 virtual bool shouldApplyBackpressure(size_t totalQueueSize, ReadableStre
am*) = 0; | |
| 86 | |
| 87 DEFINE_INLINE_VIRTUAL_TRACE() { } | |
| 88 }; | |
| 89 | |
| 90 class DefaultStrategy : public Strategy { | |
| 91 public: | |
| 92 size_t size(const typename ChunkTypeTraits::PassType& chunk, ReadableStr
eam*) override { return 1; } | |
| 93 bool shouldApplyBackpressure(size_t totalQueueSize, ReadableStream*) ove
rride { return totalQueueSize > 1; } | |
| 94 }; | |
| 95 | |
| 96 class StrictStrategy : public Strategy { | |
| 97 public: | |
| 98 size_t size(const typename ChunkTypeTraits::PassType& chunk, ReadableStr
eam*) override { return 1; } | |
| 99 bool shouldApplyBackpressure(size_t totalQueueSize, ReadableStream*) ove
rride { return true; } | |
| 100 }; | |
| 101 | |
| 102 explicit ReadableStreamImpl(UnderlyingSource* source) | |
| 103 : ReadableStreamImpl(source, new DefaultStrategy) { } | |
| 104 ReadableStreamImpl(UnderlyingSource* source, Strategy* strategy) | |
| 105 : ReadableStream(source) | |
| 106 , m_strategy(strategy) | |
| 107 , m_totalQueueSize(0) { } | |
| 108 ~ReadableStreamImpl() override { } | |
| 109 | |
| 110 // ReadableStream methods | |
| 111 ScriptPromise read(ScriptState*) override; | |
| 112 | |
| 113 bool enqueue(typename ChunkTypeTraits::PassType); | |
| 114 | |
| 115 // This function is intended to be used by internal code to withdraw | |
| 116 // queued data. This pulls all data from this stream's queue, but | |
| 117 // ReadableStream public APIs can work with the behavior (i.e. it behaves | |
| 118 // as if multiple read-one-buffer calls were made). | |
| 119 void readInternal(Deque<std::pair<typename ChunkTypeTraits::HoldType, size_t
>>& queue); | |
| 120 | |
| 121 DEFINE_INLINE_VIRTUAL_TRACE() | |
| 122 { | |
| 123 visitor->trace(m_strategy); | |
| 124 visitor->trace(m_pendingReads); | |
| 125 ReadableStream::trace(visitor); | |
| 126 } | |
| 127 | |
| 128 private: | |
| 129 using PendingReads = HeapDeque<Member<ScriptPromiseResolver>>; | |
| 130 | |
| 131 // ReadableStream methods | |
| 132 bool isQueueEmpty() const override { return m_queue.isEmpty(); } | |
| 133 void clearQueue() override | |
| 134 { | |
| 135 m_queue.clear(); | |
| 136 m_totalQueueSize = 0; | |
| 137 } | |
| 138 | |
| 139 void resolveAllPendingReadsAsDone() override | |
| 140 { | |
| 141 for (auto& resolver : m_pendingReads) { | |
| 142 ScriptState* scriptState = resolver->getScriptState(); | |
| 143 if (!scriptState->contextIsValid()) | |
| 144 continue; | |
| 145 ScriptState::Scope scope(scriptState); | |
| 146 resolver->resolve(v8IteratorResultDone(scriptState)); | |
| 147 } | |
| 148 m_pendingReads.clear(); | |
| 149 } | |
| 150 | |
| 151 void rejectAllPendingReads(DOMException* reason) override | |
| 152 { | |
| 153 for (auto& resolver : m_pendingReads) | |
| 154 resolver->reject(reason); | |
| 155 m_pendingReads.clear(); | |
| 156 } | |
| 157 | |
| 158 bool shouldApplyBackpressure() override | |
| 159 { | |
| 160 return m_strategy->shouldApplyBackpressure(m_totalQueueSize, this); | |
| 161 } | |
| 162 bool hasPendingReads() const override { return !m_pendingReads.isEmpty(); } | |
| 163 | |
| 164 Member<Strategy> m_strategy; | |
| 165 Deque<std::pair<typename ChunkTypeTraits::HoldType, size_t>> m_queue; | |
| 166 PendingReads m_pendingReads; | |
| 167 size_t m_totalQueueSize; | |
| 168 }; | |
| 169 | |
| 170 template <typename ChunkTypeTraits> | |
| 171 bool ReadableStreamImpl<ChunkTypeTraits>::enqueue(typename ChunkTypeTraits::Pass
Type chunk) | |
| 172 { | |
| 173 size_t size = m_strategy->size(chunk, this); | |
| 174 if (!enqueuePreliminaryCheck()) | |
| 175 return false; | |
| 176 | |
| 177 if (m_pendingReads.isEmpty()) { | |
| 178 m_queue.append(std::make_pair(chunk, size)); | |
| 179 m_totalQueueSize += size; | |
| 180 return enqueuePostAction(); | |
| 181 } | |
| 182 | |
| 183 ScriptPromiseResolver* resolver = m_pendingReads.takeFirst(); | |
| 184 ScriptState* scriptState = resolver->getScriptState(); | |
| 185 if (!scriptState->contextIsValid()) | |
| 186 return false; | |
| 187 ScriptState::Scope scope(scriptState); | |
| 188 resolver->resolve(v8IteratorResult(scriptState, chunk)); | |
| 189 return enqueuePostAction(); | |
| 190 } | |
| 191 | |
| 192 template <typename ChunkTypeTraits> | |
| 193 ScriptPromise ReadableStreamImpl<ChunkTypeTraits>::read(ScriptState* scriptState
) | |
| 194 { | |
| 195 if (stateInternal() == Closed) | |
| 196 return ScriptPromise::cast(scriptState, v8IteratorResultDone(scriptState
)); | |
| 197 if (stateInternal() == Errored) | |
| 198 return ScriptPromise::reject(scriptState, toV8(storedException(), script
State->context()->Global(), scriptState->isolate())); | |
| 199 | |
| 200 ASSERT(stateInternal() == Readable); | |
| 201 setIsDisturbed(); | |
| 202 if (m_queue.isEmpty()) { | |
| 203 m_pendingReads.append(ScriptPromiseResolver::create(scriptState)); | |
| 204 ScriptPromise promise = m_pendingReads.last()->promise(); | |
| 205 readInternalPostAction(); | |
| 206 return promise; | |
| 207 } | |
| 208 | |
| 209 auto pair = m_queue.takeFirst(); | |
| 210 typename ChunkTypeTraits::HoldType chunk = pair.first; | |
| 211 size_t size = pair.second; | |
| 212 ASSERT(m_totalQueueSize >= size); | |
| 213 m_totalQueueSize -= size; | |
| 214 readInternalPostAction(); | |
| 215 | |
| 216 return ScriptPromise::cast(scriptState, v8IteratorResult(scriptState, chunk)
); | |
| 217 } | |
| 218 | |
| 219 template <typename ChunkTypeTraits> | |
| 220 void ReadableStreamImpl<ChunkTypeTraits>::readInternal(Deque<std::pair<typename
ChunkTypeTraits::HoldType, size_t>>& queue) | |
| 221 { | |
| 222 // We omit the preliminary check. Check it by yourself. | |
| 223 ASSERT(stateInternal() == Readable); | |
| 224 ASSERT(m_pendingReads.isEmpty()); | |
| 225 ASSERT(queue.isEmpty()); | |
| 226 | |
| 227 setIsDisturbed(); | |
| 228 queue.swap(m_queue); | |
| 229 m_totalQueueSize = 0; | |
| 230 readInternalPostAction(); | |
| 231 } | |
| 232 | |
| 233 } // namespace blink | |
| 234 | |
| 235 #endif // ReadableStreamImpl_h | |
| OLD | NEW |