| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "core/streams/ReadableStreamReader.h" | 6 #include "core/streams/ReadableStreamReader.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/ToV8.h" |
| 10 #include "bindings/core/v8/V8IteratorResultValue.h" | 11 #include "bindings/core/v8/V8IteratorResultValue.h" |
| 11 #include "core/dom/DOMException.h" | 12 #include "core/dom/DOMException.h" |
| 12 #include "core/dom/ExceptionCode.h" | 13 #include "core/dom/ExceptionCode.h" |
| 13 #include "core/streams/ReadableStream.h" | 14 #include "core/streams/ReadableStream.h" |
| 14 | 15 |
| 15 namespace blink { | 16 namespace blink { |
| 16 | 17 |
| 17 ReadableStreamReader::ReadableStreamReader(ExecutionContext* executionContext, R
eadableStream* stream) | 18 ReadableStreamReader::ReadableStreamReader(ExecutionContext* executionContext, R
eadableStream* stream) |
| 18 : ActiveDOMObject(executionContext) | 19 : ActiveDOMObject(executionContext) |
| 19 , m_stream(stream) | 20 , m_stream(stream) |
| 20 , m_stateAfterRelease(ReadableStream::Closed) | |
| 21 , m_closed(new ClosedPromise(executionContext, this, ClosedPromise::Closed)) | 21 , m_closed(new ClosedPromise(executionContext, this, ClosedPromise::Closed)) |
| 22 { | 22 { |
| 23 suspendIfNeeded(); | 23 suspendIfNeeded(); |
| 24 ASSERT(m_stream->isLockedTo(nullptr)); | 24 ASSERT(m_stream->isLockedTo(nullptr)); |
| 25 m_stream->setReader(this); | 25 m_stream->setReader(this); |
| 26 | 26 |
| 27 if (m_stream->stateInternal() == ReadableStream::Closed || m_stream->stateIn
ternal() == ReadableStream::Errored) { | 27 if (m_stream->stateInternal() == ReadableStream::Closed) |
| 28 // If the stream is already closed or errored the created reader | 28 m_closed->resolve(ToV8UndefinedGenerator()); |
| 29 // should be closed or errored respectively. | 29 if (m_stream->stateInternal() == ReadableStream::Errored) |
| 30 releaseLock(); | 30 m_closed->reject(m_stream->storedException()); |
| 31 } | |
| 32 } | 31 } |
| 33 | 32 |
| 34 ScriptPromise ReadableStreamReader::closed(ScriptState* scriptState) | 33 ScriptPromise ReadableStreamReader::closed(ScriptState* scriptState) |
| 35 { | 34 { |
| 36 return m_closed->promise(scriptState->world()); | 35 return m_closed->promise(scriptState->world()); |
| 37 } | 36 } |
| 38 | 37 |
| 39 bool ReadableStreamReader::isActive() const | 38 bool ReadableStreamReader::isActive() const |
| 40 { | 39 { |
| 41 return m_stream->isLockedTo(this); | 40 return m_stream->isLockedTo(this); |
| 42 } | 41 } |
| 43 | 42 |
| 44 ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState) | 43 ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState) |
| 45 { | 44 { |
| 46 return cancel(scriptState, ScriptValue(scriptState, v8::Undefined(scriptStat
e->isolate()))); | 45 return cancel(scriptState, ScriptValue(scriptState, v8::Undefined(scriptStat
e->isolate()))); |
| 47 } | 46 } |
| 48 | 47 |
| 49 ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState, ScriptValue
reason) | 48 ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState, ScriptValue
reason) |
| 50 { | 49 { |
| 51 if (isActive()) | 50 if (isActive()) |
| 52 return m_stream->cancelInternal(scriptState, reason); | 51 return m_stream->cancelInternal(scriptState, reason); |
| 53 | 52 |
| 54 // A method should return a different promise on each call. | 53 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(
scriptState->isolate(), "the reader is already released")); |
| 55 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | |
| 56 ScriptPromise promise = resolver->promise(); | |
| 57 resolver->resolve(closed(scriptState).v8Value()); | |
| 58 return promise; | |
| 59 } | 54 } |
| 60 | 55 |
| 61 ScriptPromise ReadableStreamReader::read(ScriptState* scriptState) | 56 ScriptPromise ReadableStreamReader::read(ScriptState* scriptState) |
| 62 { | 57 { |
| 63 if (!isActive()) { | 58 if (!isActive()) |
| 64 ASSERT(m_stateAfterRelease == ReadableStream::Closed || m_stateAfterRele
ase == ReadableStream::Errored); | 59 return ScriptPromise::reject(scriptState, V8ThrowException::createTypeEr
ror(scriptState->isolate(), "the reader is already released")); |
| 65 if (m_stateAfterRelease == ReadableStream::Closed) { | |
| 66 // {value: undefined, done: true} | |
| 67 return ScriptPromise::cast(scriptState, v8IteratorResultDone(scriptS
tate)); | |
| 68 } | |
| 69 // A method should return a different promise on each call. | |
| 70 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptSt
ate); | |
| 71 ScriptPromise promise = resolver->promise(); | |
| 72 resolver->resolve(closed(scriptState).v8Value()); | |
| 73 return promise; | |
| 74 } | |
| 75 | 60 |
| 76 return m_stream->read(scriptState); | 61 return m_stream->read(scriptState); |
| 77 } | 62 } |
| 78 | 63 |
| 79 void ReadableStreamReader::releaseLock(ExceptionState& es) | 64 void ReadableStreamReader::releaseLock(ExceptionState& es) |
| 80 { | 65 { |
| 81 if (!isActive()) | 66 if (!isActive()) |
| 82 return; | 67 return; |
| 83 if (m_stream->hasPendingReads()) { | 68 if (m_stream->hasPendingReads()) { |
| 84 es.throwTypeError("The stream has pending read operations."); | 69 es.throwTypeError("The stream has pending read operations."); |
| 85 return; | 70 return; |
| 86 } | 71 } |
| 87 | 72 |
| 88 releaseLock(); | 73 releaseLock(); |
| 89 } | 74 } |
| 90 | 75 |
| 91 void ReadableStreamReader::releaseLock() | 76 void ReadableStreamReader::releaseLock() |
| 92 { | 77 { |
| 93 if (!isActive()) | 78 if (!isActive()) |
| 94 return; | 79 return; |
| 95 | 80 |
| 96 ASSERT(!m_stream->hasPendingReads()); | 81 ASSERT(!m_stream->hasPendingReads()); |
| 97 if (m_stream->stateInternal() == ReadableStream::Closed) { | 82 if (m_stream->stateInternal() != ReadableStream::Readable) |
| 98 m_stateAfterRelease = ReadableStream::Closed; | 83 m_closed->reset(); |
| 99 m_closed->resolve(ToV8UndefinedGenerator()); | 84 // Note: It is generally a bad idea to store world-dependent values |
| 100 } else if (m_stream->stateInternal() == ReadableStream::Errored) { | 85 // (e.g. v8::Object) in a ScriptPromiseProperty, so we use DOMException |
| 101 m_stateAfterRelease = ReadableStream::Errored; | 86 // though the spec says the promise should be rejected with a TypeError. |
| 102 m_closed->reject(m_stream->storedException()); | 87 m_closed->reject(DOMException::create(AbortError, "the reader is already rel
eased")); |
| 103 } else { | |
| 104 m_stateAfterRelease = ReadableStream::Closed; | |
| 105 m_closed->resolve(ToV8UndefinedGenerator()); | |
| 106 } | |
| 107 | 88 |
| 108 // We call setReader(nullptr) after resolving / rejecting |m_closed| | 89 // We call setReader(nullptr) after resolving / rejecting |m_closed| |
| 109 // because it affects hasPendingActivity. | 90 // because it affects hasPendingActivity. |
| 110 m_stream->setReader(nullptr); | 91 m_stream->setReader(nullptr); |
| 111 ASSERT(!isActive()); | 92 ASSERT(!isActive()); |
| 112 } | 93 } |
| 113 | 94 |
| 95 void ReadableStreamReader::close() |
| 96 { |
| 97 ASSERT(isActive()); |
| 98 m_closed->resolve(ToV8UndefinedGenerator()); |
| 99 } |
| 100 |
| 101 void ReadableStreamReader::error() |
| 102 { |
| 103 ASSERT(isActive()); |
| 104 m_closed->reject(m_stream->storedException()); |
| 105 } |
| 106 |
| 114 bool ReadableStreamReader::hasPendingActivity() const | 107 bool ReadableStreamReader::hasPendingActivity() const |
| 115 { | 108 { |
| 116 // We need to extend ReadableStreamReader's wrapper's life while it is | 109 // We need to extend ReadableStreamReader's wrapper's life while it is |
| 117 // active in order to call resolve / reject on ScriptPromiseProperties. | 110 // active in order to call resolve / reject on ScriptPromiseProperties. |
| 118 return isActive(); | 111 return isActive() && m_stream->stateInternal() == ReadableStream::Readable; |
| 119 } | 112 } |
| 120 | 113 |
| 121 void ReadableStreamReader::stop() | 114 void ReadableStreamReader::stop() |
| 122 { | 115 { |
| 123 if (isActive()) { | 116 if (isActive()) { |
| 124 // Calling |error| will release the lock. | 117 // Calling |error| will release the lock. |
| 125 m_stream->error(DOMException::create(AbortError, "The frame stops workin
g.")); | 118 m_stream->error(DOMException::create(AbortError, "The frame stops workin
g.")); |
| 126 } | 119 } |
| 127 ActiveDOMObject::stop(); | 120 ActiveDOMObject::stop(); |
| 128 } | 121 } |
| 129 | 122 |
| 130 DEFINE_TRACE(ReadableStreamReader) | 123 DEFINE_TRACE(ReadableStreamReader) |
| 131 { | 124 { |
| 132 visitor->trace(m_stream); | 125 visitor->trace(m_stream); |
| 133 visitor->trace(m_closed); | 126 visitor->trace(m_closed); |
| 134 ActiveDOMObject::trace(visitor); | 127 ActiveDOMObject::trace(visitor); |
| 135 } | 128 } |
| 136 | 129 |
| 137 } // namespace blink | 130 } // namespace blink |
| OLD | NEW |