OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "bindings/core/v8/ReadableStreamOperations.h" |
| 7 |
| 8 #include "bindings/core/v8/ExceptionState.h" |
| 9 #include "bindings/core/v8/ScriptFunction.h" |
| 10 #include "bindings/core/v8/ScriptState.h" |
| 11 #include "bindings/core/v8/V8Binding.h" |
| 12 #include "bindings/core/v8/V8BindingForTesting.h" |
| 13 #include "bindings/core/v8/V8BindingMacros.h" |
| 14 #include "bindings/core/v8/V8IteratorResultValue.h" |
| 15 #include "bindings/core/v8/V8ThrowException.h" |
| 16 #include "platform/heap/Handle.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 #include <v8.h> |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 namespace { |
| 23 |
| 24 // Unpacks |item|, stores the value of "done" member to |done| and returns |
| 25 // the value of "value" member. Returns an empty handle when errored. |
| 26 v8::MaybeLocal<v8::Value> unpack(ScriptState* scriptState, v8::Local<v8::Value>
item, bool* done) |
| 27 { |
| 28 if (!item->IsObject()) { |
| 29 V8ThrowException::throwTypeError(scriptState->isolate(), "The iteration
item is not an object."); |
| 30 return v8::MaybeLocal<v8::Value>(); |
| 31 } |
| 32 v8::Local<v8::Object> object = item.As<v8::Object>(); |
| 33 v8::Local<v8::Value> doneValue; |
| 34 if (!v8Call(object->Get(scriptState->context(), v8String(scriptState->isolat
e(), "done")), doneValue)) |
| 35 return v8::MaybeLocal<v8::Value>(); |
| 36 v8::MaybeLocal<v8::Value> r = object->Get(scriptState->context(), v8String(s
criptState->isolate(), "value")); |
| 37 if (!r.IsEmpty()) |
| 38 *done = doneValue->ToBoolean()->Value(); |
| 39 return r; |
| 40 } |
| 41 |
| 42 class NotReached : public ScriptFunction { |
| 43 public: |
| 44 static v8::Local<v8::Function> createFunction(ScriptState* scriptState) |
| 45 { |
| 46 NotReached* self = new NotReached(scriptState); |
| 47 return self->bindToV8Function(); |
| 48 } |
| 49 |
| 50 private: |
| 51 explicit NotReached(ScriptState* scriptState) |
| 52 : ScriptFunction(scriptState) |
| 53 { |
| 54 } |
| 55 |
| 56 ScriptValue call(ScriptValue) override; |
| 57 }; |
| 58 |
| 59 ScriptValue NotReached::call(ScriptValue) |
| 60 { |
| 61 EXPECT_TRUE(false) << "'Unreachable' code was reached"; |
| 62 return ScriptValue(); |
| 63 } |
| 64 |
| 65 class Iteration final : public GarbageCollectedFinalized<Iteration> { |
| 66 public: |
| 67 Iteration() |
| 68 : m_isSet(false) |
| 69 , m_isDone(false) |
| 70 , m_isValid(true) {} |
| 71 |
| 72 void set(ScriptValue v) |
| 73 { |
| 74 ASSERT(!v.isEmpty()); |
| 75 m_isSet = true; |
| 76 v8::TryCatch block(v.scriptState()->isolate()); |
| 77 v8::Local<v8::Value> value; |
| 78 if (!v8Call(unpack(v.scriptState(), v.v8Value(), &m_isDone), value)) { |
| 79 m_isValid = false; |
| 80 return; |
| 81 } |
| 82 m_value = toCoreString(value->ToString()); |
| 83 } |
| 84 |
| 85 bool isSet() const { return m_isSet; } |
| 86 bool isDone() const { return m_isDone; } |
| 87 bool isValid() const { return m_isValid; } |
| 88 const String& value() const { return m_value; } |
| 89 |
| 90 DEFINE_INLINE_TRACE() {} |
| 91 |
| 92 private: |
| 93 bool m_isSet; |
| 94 bool m_isDone; |
| 95 bool m_isValid; |
| 96 String m_value; |
| 97 }; |
| 98 |
| 99 class Function : public ScriptFunction { |
| 100 public: |
| 101 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, Iter
ation* iteration) |
| 102 { |
| 103 Function* self = new Function(scriptState, iteration); |
| 104 return self->bindToV8Function(); |
| 105 } |
| 106 |
| 107 DEFINE_INLINE_VIRTUAL_TRACE() |
| 108 { |
| 109 visitor->trace(m_iteration); |
| 110 ScriptFunction::trace(visitor); |
| 111 } |
| 112 |
| 113 private: |
| 114 Function(ScriptState* scriptState, Iteration* iteration) |
| 115 : ScriptFunction(scriptState) |
| 116 , m_iteration(iteration) |
| 117 { |
| 118 } |
| 119 |
| 120 ScriptValue call(ScriptValue value) override |
| 121 { |
| 122 m_iteration->set(value); |
| 123 return value; |
| 124 } |
| 125 |
| 126 Member<Iteration> m_iteration; |
| 127 }; |
| 128 |
| 129 class ReadableStreamOperationsTest : public ::testing::Test { |
| 130 public: |
| 131 ReadableStreamOperationsTest() : m_scope(v8::Isolate::GetCurrent()), m_block
(isolate()) {} |
| 132 ~ReadableStreamOperationsTest() override |
| 133 { |
| 134 // Execute all pending microtasks |
| 135 isolate()->RunMicrotasks(); |
| 136 EXPECT_FALSE(m_block.HasCaught()); |
| 137 } |
| 138 |
| 139 ScriptState* scriptState() const { return m_scope.scriptState(); } |
| 140 v8::Isolate* isolate() const { return scriptState()->isolate(); } |
| 141 |
| 142 v8::MaybeLocal<v8::Value> eval(const char* s) |
| 143 { |
| 144 v8::Local<v8::String> source; |
| 145 v8::Local<v8::Script> script; |
| 146 if (!v8Call(v8::String::NewFromUtf8(isolate(), s, v8::NewStringType::kNo
rmal), source)) { |
| 147 ADD_FAILURE(); |
| 148 return v8::MaybeLocal<v8::Value>(); |
| 149 } |
| 150 if (!v8Call(v8::Script::Compile(scriptState()->context(), source), scrip
t)) { |
| 151 ADD_FAILURE() << "Compilation fails"; |
| 152 return v8::MaybeLocal<v8::Value>(); |
| 153 } |
| 154 return script->Run(scriptState()->context()); |
| 155 } |
| 156 v8::MaybeLocal<v8::Value> evalWithPrintingError(const char* s) |
| 157 { |
| 158 v8::TryCatch block(isolate()); |
| 159 v8::MaybeLocal<v8::Value> r = eval(s); |
| 160 if (block.HasCaught()) { |
| 161 ADD_FAILURE() << toCoreString(block.Exception()->ToString(isolate())
).utf8().data(); |
| 162 block.ReThrow(); |
| 163 } |
| 164 return r; |
| 165 } |
| 166 |
| 167 V8TestingScope m_scope; |
| 168 v8::TryCatch m_block; |
| 169 }; |
| 170 |
| 171 TEST_F(ReadableStreamOperationsTest, IsReadableStream) |
| 172 { |
| 173 EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::U
ndefined(isolate()))); |
| 174 EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::N
ull(isolate()))); |
| 175 EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::O
bject::New(isolate()))); |
| 176 v8::Local<v8::Value> stream; |
| 177 ASSERT_TRUE(v8Call(evalWithPrintingError("new ReadableStream()"), stream)); |
| 178 EXPECT_TRUE(ReadableStreamOperations::isReadableStream(scriptState(), stream
)); |
| 179 } |
| 180 |
| 181 TEST_F(ReadableStreamOperationsTest, IsReadableStreamReaderInvalid) |
| 182 { |
| 183 EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
v8::Undefined(isolate()))); |
| 184 EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
v8::Null(isolate()))); |
| 185 EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
v8::Object::New(isolate()))); |
| 186 v8::Local<v8::Value> stream; |
| 187 ASSERT_TRUE(v8Call(evalWithPrintingError("new ReadableStream()"), stream)); |
| 188 |
| 189 EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
stream)); |
| 190 } |
| 191 |
| 192 TEST_F(ReadableStreamOperationsTest, GetReader) |
| 193 { |
| 194 v8::Local<v8::Value> stream; |
| 195 ASSERT_TRUE(v8Call(evalWithPrintingError("new ReadableStream()"), stream)); |
| 196 |
| 197 EXPECT_FALSE(ReadableStreamOperations::isLocked(scriptState(), stream)); |
| 198 ScriptValue reader; |
| 199 { |
| 200 TrackExceptionState es; |
| 201 reader = ReadableStreamOperations::getReader(scriptState(), stream, es); |
| 202 ASSERT_FALSE(es.hadException()); |
| 203 } |
| 204 EXPECT_TRUE(ReadableStreamOperations::isLocked(scriptState(), stream)); |
| 205 ASSERT_FALSE(reader.isEmpty()); |
| 206 |
| 207 EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), reade
r.v8Value())); |
| 208 EXPECT_TRUE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
reader.v8Value())); |
| 209 |
| 210 // Already locked! |
| 211 { |
| 212 TrackExceptionState es; |
| 213 reader = ReadableStreamOperations::getReader(scriptState(), stream, es); |
| 214 ASSERT_TRUE(es.hadException()); |
| 215 } |
| 216 ASSERT_TRUE(reader.isEmpty()); |
| 217 } |
| 218 |
| 219 TEST_F(ReadableStreamOperationsTest, IsDisturbed) |
| 220 { |
| 221 v8::Local<v8::Value> stream; |
| 222 ASSERT_TRUE(v8Call(evalWithPrintingError("stream = new ReadableStream()"), s
tream)); |
| 223 |
| 224 EXPECT_FALSE(ReadableStreamOperations::isDisturbed(scriptState(), stream)); |
| 225 |
| 226 ASSERT_FALSE(evalWithPrintingError("stream.cancel()").IsEmpty()); |
| 227 |
| 228 EXPECT_TRUE(ReadableStreamOperations::isDisturbed(scriptState(), stream)); |
| 229 } |
| 230 |
| 231 TEST_F(ReadableStreamOperationsTest, Read) |
| 232 { |
| 233 v8::Local<v8::Value> reader; |
| 234 ASSERT_TRUE(v8Call(evalWithPrintingError( |
| 235 "var controller;" |
| 236 "function start(c) { controller = c; }" |
| 237 "new ReadableStream({start}).getReader()"), reader)); |
| 238 ASSERT_TRUE(ReadableStreamOperations::isReadableStreamReader(scriptState(),
reader)); |
| 239 |
| 240 Iteration* it1 = new Iteration(); |
| 241 Iteration* it2 = new Iteration(); |
| 242 ReadableStreamOperations::read(scriptState(), reader).then( |
| 243 Function::createFunction(scriptState(), it1), |
| 244 NotReached::createFunction(scriptState())); |
| 245 ReadableStreamOperations::read(scriptState(), reader).then( |
| 246 Function::createFunction(scriptState(), it2), |
| 247 NotReached::createFunction(scriptState())); |
| 248 |
| 249 isolate()->RunMicrotasks(); |
| 250 EXPECT_FALSE(it1->isSet()); |
| 251 EXPECT_FALSE(it2->isSet()); |
| 252 |
| 253 ASSERT_FALSE(evalWithPrintingError("controller.enqueue('hello')").IsEmpty())
; |
| 254 isolate()->RunMicrotasks(); |
| 255 EXPECT_TRUE(it1->isSet()); |
| 256 EXPECT_TRUE(it1->isValid()); |
| 257 EXPECT_FALSE(it1->isDone()); |
| 258 EXPECT_EQ("hello", it1->value()); |
| 259 EXPECT_FALSE(it2->isSet()); |
| 260 |
| 261 ASSERT_FALSE(evalWithPrintingError("controller.close()").IsEmpty()); |
| 262 isolate()->RunMicrotasks(); |
| 263 EXPECT_TRUE(it1->isSet()); |
| 264 EXPECT_TRUE(it1->isValid()); |
| 265 EXPECT_FALSE(it1->isDone()); |
| 266 EXPECT_EQ("hello", it1->value()); |
| 267 EXPECT_TRUE(it2->isSet()); |
| 268 EXPECT_TRUE(it2->isValid()); |
| 269 EXPECT_TRUE(it2->isDone()); |
| 270 } |
| 271 |
| 272 } // namespace |
| 273 |
| 274 } // namespace blink |
| 275 |
OLD | NEW |